﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область ПрограммныйИнтерфейс

// Возвращает текущую настройку использования электронных подписей.
//
// Возвращаемое значение:
//  Булево - если Истина, электронные подписи используются.
//
Функция ИспользоватьЭлектронныеПодписи() Экспорт
	
	Возврат ОбщиеНастройки().ИспользоватьЭлектронныеПодписи;
	
КонецФункции

// Возвращает текущую настройку доступности усовершенствования подписей.
//
// Возвращаемое значение:
//  Булево - если Истина, электронные подписи используются.
//
Функция ДоступнаУсовершенствованнаяПодпись() Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебныйПовтИсп.ДоступнаУсовершенствованнаяПодпись();

КонецФункции

// Возвращает текущую настройку использования шифрования.
//
// Возвращаемое значение:
//  Булево - если Истина, шифрование используется.
//
Функция ИспользоватьШифрование() Экспорт
	
	Возврат ОбщиеНастройки().ИспользоватьШифрование;
	
КонецФункции

// Возвращает текущую настройку проверки электронных подписей на сервере.
//
// Возвращаемое значение:
//  Булево - если Истина, электронные подписи будут проверяться на сервере.
//
Функция ПроверятьЭлектронныеПодписиНаСервере() Экспорт
	
	Возврат ОбщиеНастройки().ПроверятьЭлектронныеПодписиНаСервере;
	
КонецФункции

// Возвращает текущую настройку создания электронных подписей на сервере.
// Настройка также предполагает шифрование и расшифровку на сервере.
//
// Возвращаемое значение:
//  Булево - если Истина, электронные подписи будут создаваться на сервере.
//
Функция СоздаватьЭлектронныеПодписиНаСервере() Экспорт
	
	Возврат ОбщиеНастройки().СоздаватьЭлектронныеПодписиНаСервере;
	
КонецФункции

// Получает подписи объекта и возвращает их.
//
// Параметры:
//  Объект - ОпределяемыйТип.ПодписанныйОбъект - ссылка на подписанный объект.
//             Объект должен иметь реквизит ПодписанЭП.
//
//  ПорядковыйНомер - Число
//                  - Массив из Число
//  ВозвращатьДанныеМЧД - Булево - если Истина и для подписи есть МЧД будет заполнено свойство РезультатПроверкиПодписиПоМЧД
//
// Возвращаемое значение:
//  Массив из см. ЭлектроннаяПодписьКлиентСервер.НовыеСвойстваПодписи 
//
Функция УстановленныеПодписи(Объект, ПорядковыйНомер = Неопределено, ВозвращатьДанныеМЧД = Ложь) Экспорт
	
	ПроверитьПараметрОбъект(Объект, "ЭлектроннаяПодпись.УстановленныеПодписи", Истина);
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
		МодульУправлениеДоступом.ПроверитьЧтениеРазрешено(Объект);
	КонецЕсли;
	
	Если ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Объект)) Тогда
		ОбъектСсылка = Объект;
	Иначе
		ОбъектСсылка = Объект.Ссылка;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос = Новый Запрос;
	Запрос.Текст =
		"ВЫБРАТЬ
		|	ЭлектронныеПодписи.Подпись КАК Подпись,
		|	ЭлектронныеПодписи.ПорядковыйНомер КАК ПорядковыйНомер,
		|	ЭлектронныеПодписи.УстановившийПодпись КАК УстановившийПодпись,
		|	ЭлектронныеПодписи.Комментарий КАК Комментарий,
		|	ЭлектронныеПодписи.ИмяФайлаПодписи КАК ИмяФайлаПодписи,
		|	ЭлектронныеПодписи.ДатаПодписи КАК ДатаПодписи,
		|	ЭлектронныеПодписи.ДатаПроверкиПодписи КАК ДатаПроверкиПодписи,
		|	ЭлектронныеПодписи.ПодписьВерна КАК ПодписьВерна,
		|	ЭлектронныеПодписи.Сертификат КАК Сертификат,
		|	ЭлектронныеПодписи.Отпечаток КАК Отпечаток,
		|	ЭлектронныеПодписи.КомуВыданСертификат КАК КомуВыданСертификат,
		|	ЭлектронныеПодписи.ТипПодписи КАК ТипПодписи,
		|	ЭлектронныеПодписи.ТребуетсяПроверка КАК ТребуетсяПроверка,
		|	ЭлектронныеПодписи.СрокДействияПоследнейМеткиВремени КАК СрокДействияПоследнейМеткиВремени,
		|	ЭлектронныеПодписи.ИдентификаторПодписи КАК ИдентификаторПодписи
		|ИЗ
		|	РегистрСведений.ЭлектронныеПодписи КАК ЭлектронныеПодписи
		|ГДЕ
		|	ЭлектронныеПодписи.ПодписанныйОбъект = &ПодписанныйОбъект
		|	И ЭлектронныеПодписи.ПорядковыйНомер В(&ПорядковыйНомер)
		|
		|УПОРЯДОЧИТЬ ПО
		|	ПорядковыйНомер";
	
	Запрос.УстановитьПараметр("ПодписанныйОбъект", ОбъектСсылка);
	
	Если ПорядковыйНомер = Неопределено Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "И ЭлектронныеПодписи.ПорядковыйНомер В(&ПорядковыйНомер)", "");
	Иначе
		Запрос.УстановитьПараметр("ПорядковыйНомер", ПорядковыйНомер);
	КонецЕсли;
	
	РезультатЗапроса = Запрос.Выполнить();
	ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
	
	// Локализация
	
	ВыборкаДетальныеЗаписиМЧД = Неопределено;
	
	Если Не РезультатЗапроса.Пустой() И ВозвращатьДанныеМЧД
		И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.МашиночитаемыеДоверенности") Тогда
		
		ИдентификаторыПодписей = Новый Массив;
		Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
			ИдентификаторыПодписей.Добавить(ВыборкаДетальныеЗаписи.ИдентификаторПодписи);
		КонецЦикла;
		ВыборкаДетальныеЗаписи.Сбросить();
		
		МодульМашиночитаемыеДоверенностиФНССлужебный = ОбщегоНазначения.ОбщийМодуль("МашиночитаемыеДоверенностиФНССлужебный");
		ВыборкаДетальныеЗаписиМЧД = МодульМашиночитаемыеДоверенностиФНССлужебный.МЧДПодписей(ОбъектСсылка, ИдентификаторыПодписей);
		
	КонецЕсли;
	
	// Конец Локализация
	
	МассивЭлектронныхПодписей = Новый Массив;
	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		СвойстваПодписи = ЭлектроннаяПодписьКлиентСервер.НовыеСвойстваПодписи();
		СвойстваПодписи.ПодписанныйОбъект = Объект;
		ЗаполнитьЗначенияСвойств(СвойстваПодписи, ВыборкаДетальныеЗаписи);
		СвойстваПодписи.Подпись = СвойстваПодписи.Подпись.Получить();
		
		// Локализация
		
		Если ВыборкаДетальныеЗаписиМЧД <> Неопределено Тогда
			МодульМашиночитаемыеДоверенностиФНССлужебный.ЗаполнитьДанныеМЧДПодписи(СвойстваПодписи, ВыборкаДетальныеЗаписиМЧД);
		КонецЕсли;
		
		// Конец Локализация
		
		МассивЭлектронныхПодписей.Добавить(СвойстваПодписи);
	КонецЦикла;
	
	Возврат МассивЭлектронныхПодписей;
	
КонецФункции

// Добавляет подпись к объекту и записывает его.
// Устанавливает реквизиту ПодписанЭП значение Истина.
// 
// Параметры:
//  Объект - ОпределяемыйТип.ПодписанныйОбъект - по ссылке будет получен объект,
//               заблокирован, изменен, записан. Объект должен иметь реквизит ПодписанЭП.
//           Либо передать сразу объект указанного выше типа, тогда он
//           будет изменен без блокировки и без записи.
//
//  СвойстваПодписи - Строка - адрес временного хранилища, содержащий описанную ниже структуру.
//                  - Структура - см. ЭлектроннаяПодписьКлиентСервер.НовыеСвойстваПодписи.
//                  - Массив из Строка
//                  - Массив из см. ЭлектроннаяПодписьКлиентСервер.НовыеСвойстваПодписи.
//
//  ИдентификаторФормы - УникальныйИдентификатор - идентификатор формы, используемый для блокировки,
//                       если передана ссылка на объект.
//
//  ВерсияОбъекта      - Строка - версия данных объекта, если передана ссылка на объект, используемая
//                       для блокировки объекта перед записью с учетом того, что подписание
//                       выполняется на клиенте и за время подписания объект мог быть изменен.
//
//  ЗаписанныйОбъект   - Произвольный - объект, который был получен и записан, если передавалась ссылка.
//
Процедура ДобавитьПодпись(Объект, Знач СвойстваПодписи, ИдентификаторФормы = Неопределено,
			ВерсияОбъекта = Неопределено, ЗаписанныйОбъект = Неопределено) Экспорт
	
	ПроверитьПараметрОбъект(Объект, "ЭлектроннаяПодпись.ДобавитьПодпись");
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
		МодульУправлениеДоступом.ПроверитьИзменениеРазрешено(Объект);
	КонецЕсли;
	
	Если ТипЗнч(СвойстваПодписи) = Тип("Строка") Тогда
		СвойстваПодписи = ПолучитьИзВременногоХранилища(СвойстваПодписи);
		
	ИначеЕсли ТипЗнч(СвойстваПодписи) = Тип("Массив") Тогда
		ИндексПоследнего = СвойстваПодписи.Количество()-1;
		Для Индекс = 0 По ИндексПоследнего Цикл
			Если ТипЗнч(СвойстваПодписи[Индекс]) = Тип("Строка") Тогда
				СвойстваПодписи[Индекс] = ПолучитьИзВременногоХранилища(СвойстваПодписи[Индекс]);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	ЭтоСсылка = ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Объект));
	
	НачатьТранзакцию();
	Попытка
		
		Если ЭтоСсылка Тогда
			ЗаблокироватьДанныеДляРедактирования(Объект, ВерсияОбъекта, ИдентификаторФормы);
			ОбъектДанных = Объект.ПолучитьОбъект();
		Иначе
			ОбъектДанных = Объект;
		КонецЕсли;
		
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить(ОбъектДанных.Метаданные().ПолноеИмя());
		ЭлементБлокировки.УстановитьЗначение("Ссылка", ОбъектДанных.Ссылка);
		
		Блокировка.Заблокировать();
		
		СообщениеЖурналаРегистрации = "";
		
		ДобавитьСтрокиПодписи(ОбъектДанных, СвойстваПодписи, СообщениеЖурналаРегистрации);
		
		Если Не ОбъектДанных.ПодписанЭП
		   И (ТипЗнч(СвойстваПодписи) <> Тип("Массив")
		      Или СвойстваПодписи.Количество() > 0) Тогда
			
			ОбъектДанных.ПодписанЭП = Истина;
		КонецЕсли;
		
		Если ЭтоСсылка Тогда
			// Чтобы определить, что это запись с целью добавления/удаления подписи.
			ОбъектДанных.ДополнительныеСвойства.Вставить("ЗаписьПодписанногоОбъекта", Истина);
			Если ОбъектДанных.Модифицированность() Тогда
				ОбъектДанных.Записать();
			КонецЕсли;
			РазблокироватьДанныеДляРедактирования(Объект.Ссылка, ИдентификаторФормы);
			ЗаписанныйОбъект = ОбъектДанных;
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Если ЗначениеЗаполнено(СообщениеЖурналаРегистрации) Тогда
			ЗаписьЖурналаРегистрации(
				НСтр("ru = 'Электронная подпись.Ошибка добавления подписи'", ОбщегоНазначения.КодОсновногоЯзыка()),
				УровеньЖурналаРегистрации.Информация,
				Объект.Метаданные(),
				Объект.Ссылка,
				СообщениеЖурналаРегистрации + "
				|
				|" + ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
		КонецЕсли;
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Обновляет подпись объекта.
// 
// Параметры:
//  Объект - ОпределяемыйТип.ПодписанныйОбъект - ссылка на подписанный объект,
//             для которого требуется обновить подпись.
//
//  СвойстваПодписи - Строка - адрес временного хранилища, содержащий описанную ниже структуру.
//                  - Структура - см. ЭлектроннаяПодписьКлиентСервер.НовыеСвойстваПодписи.
//  ОбновитьПоПорядковомуНомеру - Булево - (по умолчанию Ложь) подпись обновляется по порядковому номеру,
//                                т.к. двоичные данные подписи изменились при усовершенствовании.
//
Процедура ОбновитьПодпись(Объект, Знач СвойстваПодписи, ОбновитьПоПорядковомуНомеру = Ложь) Экспорт
	
	ПроверитьПараметрОбъект(Объект, "ЭлектроннаяПодпись.ОбновитьПодпись", Истина);
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
		МодульУправлениеДоступом.ПроверитьИзменениеРазрешено(Объект);
	КонецЕсли;
	
	Если ТипЗнч(СвойстваПодписи) = Тип("Строка") Тогда
		СвойстваПодписи = ПолучитьИзВременногоХранилища(СвойстваПодписи);
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЭлектронныеПодписи");
	ЭлементБлокировки.УстановитьЗначение("ПодписанныйОбъект", Объект);
	// Локализация
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.МашиночитаемыеДоверенности") Тогда
		МодульМашиночитаемыеДоверенностиФНССлужебный = ОбщегоНазначения.ОбщийМодуль(
					"МашиночитаемыеДоверенностиФНССлужебный");
		МодульМашиночитаемыеДоверенностиФНССлужебный.ДобавитьБлокировку(Блокировка, Объект);
	КонецЕсли;
	// Конец Локализация
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		Если ОбновитьПоПорядковомуНомеру Тогда
			ПодписиОбъекта = УстановленныеПодписи(Объект, СвойстваПодписи.ПорядковыйНомер);
		Иначе
			ПодписиОбъекта = УстановленныеПодписи(Объект);
		КонецЕсли;
		Для Каждого ПодписьОбъекта Из ПодписиОбъекта Цикл
			ДвоичныеДанныеПодписи = ПодписьОбъекта.Подпись;
			РезультатПроверкиПодписиПоМЧД = Неопределено;
			Если ОбновитьПоПорядковомуНомеру И ПодписьОбъекта.ПорядковыйНомер = СвойстваПодписи.ПорядковыйНомер 
				// Если двоичные данные совпадают, то обновляем подпись.
				Или ДвоичныеДанныеПодписи = СвойстваПодписи.Подпись Тогда
					
				НаборЗаписей = РегистрыСведений.ЭлектронныеПодписи.СоздатьНаборЗаписей();
				НаборЗаписей.Отбор.ПорядковыйНомер.Установить(СвойстваПодписи.ПорядковыйНомер);
				НаборЗаписей.Отбор.ПодписанныйОбъект.Установить(Объект);
				НаборЗаписей.ДополнительныеСвойства.Вставить("ОбновлениеПодписи");
				НаборЗаписей.Прочитать();
				
				ОбновляемаяПодпись = НаборЗаписей[0];
				
				Если ОбновитьПоПорядковомуНомеру Тогда
					Для Каждого КлючИЗначение Из СвойстваПодписи Цикл
						Если КлючИЗначение.Ключ = "Сертификат"
							Или КлючИЗначение.Ключ = "ПодписанныйОбъект"
							Или КлючИЗначение.Ключ = "ДатаПодписи"
							Или КлючИЗначение.Ключ = "ПорядковыйНомер"
							Или КлючИЗначение.Ключ = "ИдентификаторПодписи" Тогда
							Продолжить;
						КонецЕсли;
						
						Если КлючИЗначение.Ключ = "РезультатПроверкиПодписиПоМЧД" Тогда
							Если ЗначениеЗаполнено(КлючИЗначение.Значение) Тогда
								РезультатПроверкиПодписиПоМЧД = КлючИЗначение.Значение;
							КонецЕсли;
							Продолжить;
						КонецЕсли;
						
						Если КлючИЗначение.Значение <> Неопределено Тогда
							Если КлючИЗначение.Ключ = "Подпись" Тогда
								ОбновляемаяПодпись.Подпись = Новый ХранилищеЗначения(КлючИЗначение.Значение);
							Иначе
								ОбновляемаяПодпись[КлючИЗначение.Ключ] = КлючИЗначение.Значение;
							КонецЕсли;
						КонецЕсли;
					КонецЦикла;
				Иначе
					
					Для Каждого КлючИЗначение Из СвойстваПодписи Цикл
						Если КлючИЗначение.Ключ = "Сертификат"
							Или КлючИЗначение.Ключ = "Подпись"
							Или КлючИЗначение.Ключ = "ИдентификаторПодписи"
								И ЗначениеЗаполнено(ОбновляемаяПодпись.ИдентификаторПодписи) Тогда
							Продолжить;
						КонецЕсли;
						
						Если КлючИЗначение.Ключ = "РезультатПроверкиПодписиПоМЧД" Тогда
							Если ЗначениеЗаполнено(КлючИЗначение.Значение) Тогда
								РезультатПроверкиПодписиПоМЧД = КлючИЗначение.Значение;
							КонецЕсли;
							Продолжить;
						КонецЕсли;
						
						Если КлючИЗначение.Значение <> Неопределено Тогда
							ОбновляемаяПодпись[КлючИЗначение.Ключ] = КлючИЗначение.Значение;
						КонецЕсли;
					КонецЦикла;
					
				КонецЕсли;
				
				Если ЗначениеЗаполнено(СвойстваПодписи.Сертификат) И Не ЗначениеЗаполнено(
					ОбновляемаяПодпись.Сертификат.Получить()) Тогда

					Если ТипЗнч(СвойстваПодписи.Сертификат) = Тип("ХранилищеЗначения") Тогда
						ОбновляемаяПодпись.Сертификат = СвойстваПодписи.Сертификат;
					Иначе
						ОбновляемаяПодпись.Сертификат = Новый ХранилищеЗначения(СвойстваПодписи.Сертификат,
							Новый СжатиеДанных(9));
					КонецЕсли;

				КонецЕсли;
				
				НаборЗаписей.Записать(Истина);
			КонецЕсли;
			
			// Локализация
			Если ЗначениеЗаполнено(РезультатПроверкиПодписиПоМЧД) И ОбщегоНазначения.ПодсистемаСуществует(
				"СтандартныеПодсистемы.МашиночитаемыеДоверенности") Тогда
		
				МодульМашиночитаемыеДоверенностиФНССлужебный.ОбновитьМашиночитаемуюДоверенностьПодписи(
					Объект, ОбновляемаяПодпись.ИдентификаторПодписи, РезультатПроверкиПодписиПоМЧД);

			КонецЕсли;
			// Конец Локализация
			
		КонецЦикла;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Удаляет подпись объекта и записывает его.
// 
// Параметры:
//  Объект - ОпределяемыйТип.ПодписанныйОбъект - по ссылке будет получен объект,
//               заблокирован, изменен, записан. Объект должен иметь реквизит ПодписанЭП.
//           Либо передать сразу объект указанного выше типа, тогда он
//           будет изменен без блокировки и без записи.
// 
//  ПорядковыйНомер      - Число - порядковый номер подписи.
//                       - Массив - значения указанного выше типа.
//
//  ИдентификаторФормы - УникальныйИдентификатор - идентификатор формы, используемый для блокировки,
//                       если передана ссылка на объект.
//
//  ВерсияОбъекта      - Строка - версия данных объекта, если передана ссылка на объект, используемая
//                       для блокировки объекта перед записью, с учетом того, что подписание
//                       выполняется на клиенте и за время подписания объект мог быть изменен.
//
//  ЗаписанныйОбъект   - Произвольный - объект, который был получен и записан, если передавалась ссылка.
//
Процедура УдалитьПодпись(Объект, ПорядковыйНомер, ИдентификаторФормы = Неопределено,
			ВерсияОбъекта = Неопределено, ЗаписанныйОбъект = Неопределено) Экспорт
	
	ПроверитьПараметрОбъект(Объект, "ЭлектроннаяПодпись.УдалитьПодпись");
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
		МодульУправлениеДоступом.ПроверитьИзменениеРазрешено(Объект);
	КонецЕсли;
	
	ЭтоСсылка = ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Объект));
	НачатьТранзакцию();
	Попытка
		Если ЭтоСсылка Тогда
			ЗаблокироватьДанныеДляРедактирования(Объект, ВерсияОбъекта, ИдентификаторФормы);
			ОбъектДанных = Объект.ПолучитьОбъект();
		Иначе
			ОбъектДанных = Объект;
		КонецЕсли;
		
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить(ОбъектДанных.Метаданные().ПолноеИмя());
		ЭлементБлокировки.УстановитьЗначение("Ссылка", ОбъектДанных.Ссылка);
		Блокировка.Заблокировать();
		
		СообщениеЖурналаРегистрации = "";
		
		УдалитьСтрокиПодписи(ОбъектДанных, ПорядковыйНомер, СообщениеЖурналаРегистрации);
		
		ОбновитьНумерациюПодписей(ОбъектДанных);
		
		Если ЭтоСсылка Тогда
			// Чтобы определить, что это запись с целью добавления/удаления подписи.
			ОбъектДанных.ДополнительныеСвойства.Вставить("ЗаписьПодписанногоОбъекта", Истина);
			Если ОбъектДанных.Модифицированность() Тогда
				ОбъектДанных.Записать();
			КонецЕсли;
			РазблокироватьДанныеДляРедактирования(Объект.Ссылка, ИдентификаторФормы);
			ЗаписанныйОбъект = ОбъектДанных;
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Если ЗначениеЗаполнено(СообщениеЖурналаРегистрации) Тогда
			ЗаписьЖурналаРегистрации(
				НСтр("ru = 'Электронная подпись.Ошибка удаления подписи'", ОбщегоНазначения.КодОсновногоЯзыка()),
				УровеньЖурналаРегистрации.Информация,
				Объект.Метаданные(),
				Объект.Ссылка,
				СообщениеЖурналаРегистрации + "
				|
				|" + ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
		КонецЕсли;
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Получает массив сертификатов шифрования.
// Параметры:
//  Объект - ОпределяемыйТип.ПодписанныйОбъект - ссылка на зашифрованный объект.
//
// Возвращаемое значение:
//   Массив - массив структур.
//
Функция СертификатыШифрования(Объект) Экспорт
	
	ПроверитьПараметрОбъект(Объект, "ЭлектроннаяПодпись.СертификатыШифрования", Истина);
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
		МодульУправлениеДоступом.ПроверитьЧтениеРазрешено(Объект);
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	СертификатыШифрования.Представление,
		|	СертификатыШифрования.Отпечаток,
		|	СертификатыШифрования.Сертификат,
		|	СертификатыШифрования.ПорядковыйНомер КАК ПорядковыйНомер
		|ИЗ
		|	РегистрСведений.СертификатыШифрования КАК СертификатыШифрования
		|ГДЕ
		|	СертификатыШифрования.ЗашифрованныйОбъект = &ЗашифрованныйОбъект
		|
		|УПОРЯДОЧИТЬ ПО
		|	ПорядковыйНомер";
	
	Запрос.УстановитьПараметр("ЗашифрованныйОбъект", Объект);
	
	РезультатЗапроса = Запрос.Выполнить();
	
	ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
	
	МассивСертификатовШифрования = Новый Массив;
	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		ОтпечатокСтруктура = Новый Структура;
		ОтпечатокСтруктура.Вставить("Отпечаток",       ВыборкаДетальныеЗаписи.Отпечаток);
		ОтпечатокСтруктура.Вставить("Представление",   ВыборкаДетальныеЗаписи.Представление);
		ОтпечатокСтруктура.Вставить("Сертификат",      ВыборкаДетальныеЗаписи.Сертификат.Получить());
		ОтпечатокСтруктура.Вставить("ПорядковыйНомер", ВыборкаДетальныеЗаписи.ПорядковыйНомер);
		МассивСертификатовШифрования.Добавить(ОтпечатокСтруктура);
	КонецЦикла;
	
	Возврат МассивСертификатовШифрования;

КонецФункции

// Помещает сертификаты шифрования в регистр сведений и записывает объект.
// Устанавливает реквизит Зашифрован по наличию сертификатов в регистре сведений СертификатаШифрования.
// 
// Параметры:
//  Объект - ОпределяемыйТип.ПодписанныйОбъект - по ссылке будет получен объект,
//               заблокирован, изменен, записан. Объект должен иметь реквизит Зашифрован.
//           Либо передать сразу объект указанного выше типа, тогда он
//           будет изменен без блокировки и без записи.
//
//  СертификатыШифрования - Строка - адрес временного хранилища, содержащий описанный ниже массив.
//                        - Массив - массив описанных ниже структур:
//                             * Отпечаток     - Строка - отпечаток сертификата в формате строки Base64.
//                             * Представление - Строка - сохраненное представление субъекта,
//                                                  полученное из двоичных данных сертификата.
//                             * Сертификат    - ДвоичныеДанные - содержит выгрузку сертификата,
//                                                  который использовался для шифрования.
//
//  ИдентификаторФормы - УникальныйИдентификатор - идентификатор формы, используемый для блокировки,
//                       если передана ссылка на объект.
//
//  ВерсияОбъекта      - Строка - версия данных объекта, если передана ссылка на объект, используемая
//                       для блокировки объекта перед записью, с учетом того, что подписание
//                       выполняется на клиенте и за время подписания объект мог быть изменен.
//
//  ЗаписанныйОбъект   - Произвольный - объект, который был получен и записан, если передавалась ссылка.
//
Процедура ЗаписатьСертификатыШифрования(Объект, Знач СертификатыШифрования, ИдентификаторФормы = Неопределено,
	ВерсияОбъекта = Неопределено, ЗаписанныйОбъект = Неопределено) Экспорт
	
	ПроверитьПараметрОбъект(Объект, "ЭлектроннаяПодпись.ЗаписатьСертификатыШифрования", Ложь);
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом") Тогда
		МодульУправлениеДоступом = ОбщегоНазначения.ОбщийМодуль("УправлениеДоступом");
		МодульУправлениеДоступом.ПроверитьИзменениеРазрешено(Объект);
	КонецЕсли;
	
	ЭтоСсылка = ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Объект));
	ОбъектСсылка = ?(ЭтоСсылка, Объект, Объект.Ссылка);
	
	Если ТипЗнч(СертификатыШифрования) = Тип("Строка") Тогда
		СертификатыШифрования = ПолучитьИзВременногоХранилища(СертификатыШифрования);
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		Если ЭтоСсылка Тогда
			ЗаблокироватьДанныеДляРедактирования(ОбъектСсылка, ВерсияОбъекта, ИдентификаторФормы);
			ОбъектДанных = Объект.ПолучитьОбъект();// СправочникОбъект, ДокументОбъект, ПланВидовХарактеристикОбъект, ПланСчетовОбъект, ПланВидовРасчетаОбъект 
		Иначе
			ОбъектДанных = Объект;
		КонецЕсли;
		
		УстановитьПривилегированныйРежим(Истина);
		НаборЗаписей = РегистрыСведений.СертификатыШифрования.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.ЗашифрованныйОбъект.Установить(ОбъектДанных.Ссылка);
		ПорядковыйНомер = 1;
		Для Каждого СертификатШифрования Из СертификатыШифрования Цикл
			НовыйСертификат = НаборЗаписей.Добавить();
			НовыйСертификат.ЗашифрованныйОбъект = ОбъектДанных.Ссылка;
			ЗаполнитьЗначенияСвойств(НовыйСертификат, СертификатШифрования);
			НовыйСертификат.ПорядковыйНомер = ПорядковыйНомер;
			ПорядковыйНомер = ПорядковыйНомер + 1;
		КонецЦикла;
		
		ОбъектДанных.Зашифрован = НаборЗаписей.Количество() > 0;
		
		НаборЗаписей.Записать();
		Если ЭтоСсылка Тогда
			РазблокироватьДанныеДляРедактирования(ОбъектСсылка, ИдентификаторФормы);
			ЗаписанныйОбъект = ОбъектДанных;
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Возвращает дату, извлеченную из двоичных данных подписи, или Неопределено.
//
// Параметры:
//  Подпись - ДвоичныеДанные - данные подписи из которых нужно извлечь дату.
//  ПривестиКЧасовомуПоясуСеанса - Булево - привести универсальное время к времени сеанса.
//
// Возвращаемое значение:
//  Дата - успешно извлеченная дата подписи.
//  Неопределено - не удалось извлечь дату из данных подписи.
//
Функция ДатаПодписания(Подпись, ПривестиКЧасовомуПоясуСеанса = Истина) Экспорт
	
	ДатаПодписания = ЭлектроннаяПодписьСлужебныйКлиентСервер.ДатаПодписанияУниверсальная(Подпись);
	
	Если ДатаПодписания = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если ПривестиКЧасовомуПоясуСеанса Тогда
		ДатаПодписания = МестноеВремя(ДатаПодписания, ЧасовойПоясСеанса());
	КонецЕсли;
	
	Возврат ДатаПодписания;
	
КонецФункции

// Выполняет поиск сертификата в справочнике, возвращает ссылку, если сертификат найден.
//
// Параметры:
//  Сертификат - СертификатКриптографии - сертификат.
//             - ДвоичныеДанные - двоичные данные сертификата.
//             - Строка - строка (28) - отпечаток сертификата в формате Base64.
//             - Строка      - адрес временного хранилища, содержащего двоичные данные сертификата.
//
// Возвращаемое значение:
//  Неопределено - сертификат не существует в справочнике.
//  СправочникСсылка.СертификатыКлючейЭлектроннойПодписиИШифрования - ссылка на найденный сертификат.
//
Функция СсылкаНаСертификат(Знач Сертификат) Экспорт
	
	Если ТипЗнч(Сертификат) = Тип("Строка") И ЭтоАдресВременногоХранилища(Сертификат) Тогда
		Сертификат = ПолучитьИзВременногоХранилища(Сертификат);
	КонецЕсли;
	
	Если ТипЗнч(Сертификат) = Тип("ДвоичныеДанные") Тогда
		Сертификат = Новый СертификатКриптографии(Сертификат);
	КонецЕсли;
	
	Если ТипЗнч(Сертификат) = Тип("СертификатКриптографии") Тогда
		ОтпечатокСтрокой = Base64Строка(Сертификат.Отпечаток);
	Иначе
		ОтпечатокСтрокой = Строка(Сертификат);
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Отпечаток", ОтпечатокСтрокой);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Сертификаты.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.СертификатыКлючейЭлектроннойПодписиИШифрования КАК Сертификаты
	|ГДЕ
	|	Сертификаты.Отпечаток = &Отпечаток";
	
	Выборка = Запрос.Выполнить().Выбрать();
	
	Если Выборка.Следующий() Тогда
		Возврат Выборка.Ссылка;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Позволяет создать и обновить элемент справочника СертификатыКлючейЭлектроннойПодписиИШифрования по
// указанному сертификату криптографии.
// Для добавления сертификата на клиенте см. ЭлектроннаяПодписьКлиент.ДобавитьСертификат.
//
// Параметры:
//  Сертификат - СертификатКриптографии - сертификат.
//             - ДвоичныеДанные - двоичные данные сертификата.
//             - Строка - адрес временного хранилища, содержащего двоичные данные сертификата.
//
//  ДополнительныеПараметры - Неопределено - без дополнительных параметров.
//                          - Структура - с произвольным составом из следующих свойств:
//      * Наименование - Строка - представление сертификата в списке.
//
//      * Пользователь - СправочникСсылка.Пользователи - пользователь, которому принадлежит сертификат.
//                       Значение используется при получении списка личных сертификатов пользователя
//                       в формах подписания и шифрования данных.
//
//      * Организация     - ОпределяемыйТип.Организация - организация, к которой относится сертификат.
//      * ФизическоеЛицо  - ОпределяемыйТип.ФизическоеЛицо - лицо, которому выдан сертификат.
//
//      * Программа - СправочникСсылка.ПрограммыЭлектроннойПодписиИШифрования - программа, которая
//                      требуется для подписания и расшифровки.
//
//      * ВводитьПарольВПрограммеЭлектроннойПодписи - Булево - флажок "Вводить пароль в программе электронной подписи",
//                      требуется Истина, когда сертификат был установлен на компьютере с усиленной
//                      защитой закрытого ключа, которая означает поддержку только пустого пароля на
//                      уровне 1С:Предприятия (пароль у пользователя не запрашивается - это делает
//                      операционная система, которая не принимает от 1С:Предприятия непустой пароль).
//
// Возвращаемое значение:
//  СправочникСсылка.СертификатыКлючейЭлектроннойПодписиИШифрования - ссылка на сертификат.
// 
Функция ЗаписатьСертификатВСправочник(Знач Сертификат, ДополнительныеПараметры = Неопределено) Экспорт
	
	Если ТипЗнч(ДополнительныеПараметры) <> Тип("Структура") Тогда
		ДополнительныеПараметры = Новый Структура;
	КонецЕсли;
	
	Если ТипЗнч(Сертификат) = Тип("Строка") И ЭтоАдресВременногоХранилища(Сертификат) Тогда
		ДвоичныеДанныеСертификата = ПолучитьИзВременногоХранилища(Сертификат);
	
	ИначеЕсли ТипЗнч(Сертификат) = Тип("ДвоичныеДанные") Тогда
		ДвоичныеДанныеСертификата = Сертификат;
	КонецЕсли;
	
	Если ДвоичныеДанныеСертификата = Неопределено Тогда
		СертификатКриптографии = Сертификат;
		ДвоичныеДанныеСертификата = СертификатКриптографии.Выгрузить();
	Иначе
		СертификатКриптографии = Новый СертификатКриптографии(ДвоичныеДанныеСертификата);
	КонецЕсли;
	
	Если ДополнительныеПараметры.Свойство("СсылкаНаСертификат") Тогда
		СертификатСсылка = ДополнительныеПараметры.СсылкаНаСертификат;
	Иначе
		СертификатСсылка = СсылкаНаСертификат(Сертификат);
	КонецЕсли;
	
	ПравоДоступаДобавление = ПравоДоступа("Добавление", Метаданные.Справочники.СертификатыКлючейЭлектроннойПодписиИШифрования);
	
	Если Не ПравоДоступаДобавление И Не ЗначениеЗаполнено(СертификатСсылка) Тогда
		ВызватьИсключение(НСтр("ru = 'Недостаточно прав для использования сертификата, отсутствующего в справочнике.'"));
	КонецЕсли;
	
	РазрешенныеПоляДляИзменения = Неопределено;
	Если Не ПравоДоступаДобавление Тогда
		РазрешенныеПоляДляИзменения = Новый Массив;
		РазрешенныеПоляДляИзменения.Добавить("Организация");
		РазрешенныеПоляДляИзменения.Добавить("Пользователь");
		РазрешенныеПоляДляИзменения.Добавить("Программа");
		РазрешенныеПоляДляИзменения.Добавить("ВводитьПарольВПрограммеЭлектроннойПодписи");
		УстановитьПривилегированныйРежим(Истина);
	КонецЕсли;
		
	НачатьТранзакцию();
	Попытка
		
		Если ЗначениеЗаполнено(СертификатСсылка) Тогда
			
			Блокировка = Новый БлокировкаДанных;
			ЭлементБлокировки = Блокировка.Добавить("Справочник.СертификатыКлючейЭлектроннойПодписиИШифрования");
			ЭлементБлокировки.УстановитьЗначение("Ссылка", СертификатСсылка);
			
			Блокировка.Заблокировать();
			
			СертификатОбъект = СертификатСсылка.ПолучитьОбъект();
			ОбновитьЗначение(СертификатОбъект.ПометкаУдаления, Ложь);
			
		Иначе
			СертификатОбъект = Справочники.СертификатыКлючейЭлектроннойПодписиИШифрования.СоздатьЭлемент();
			СертификатОбъект.ДанныеСертификата = Новый ХранилищеЗначения(ДвоичныеДанныеСертификата);
			СертификатОбъект.Отпечаток = Base64Строка(СертификатКриптографии.Отпечаток);
			
			СертификатОбъект.Добавил = Пользователи.АвторизованныйПользователь();
		КонецЕсли;
		
		Если СертификатОбъект.ДанныеСертификата.Получить() <> ДвоичныеДанныеСертификата Тогда
			Если ПравоДоступаДобавление Тогда
				СертификатОбъект.ДанныеСертификата = Новый ХранилищеЗначения(ДвоичныеДанныеСертификата);
			Иначе
				ОтменитьТранзакцию();
				ВызватьИсключение(НСтр("ru = 'Недостаточно прав для изменения данных сертификата.'"));
			КонецЕсли;
		КонецЕсли;
		
		СвойстваСертификата = Неопределено;
		Если ПравоДоступаДобавление Тогда
			СвойстваСертификата = СвойстваСертификата(СертификатКриптографии);
			
			ОбновитьЗначение(СертификатОбъект.Подписание,     СвойстваСертификата.Подписание);
			ОбновитьЗначение(СертификатОбъект.Шифрование,     СвойстваСертификата.Шифрование);
			ОбновитьЗначение(СертификатОбъект.КомуВыдан,      СвойстваСертификата.КомуВыдан);
			ОбновитьЗначение(СертификатОбъект.КемВыдан,       СвойстваСертификата.КемВыдан);
			ОбновитьЗначение(СертификатОбъект.ДействителенДо, СвойстваСертификата.ДействителенДо);
			
			СвойстваСубъекта = СвойстваСубъектаСертификата(СертификатКриптографии);
			
			Если СвойстваСубъекта.Свойство("Отчество") Тогда
				ИмяИОтчествоСубъекта = ?(ЗначениеЗаполнено(СвойстваСубъекта.Имя), СвойстваСубъекта.Имя, "")
					+ ?(ЗначениеЗаполнено(СвойстваСубъекта.Отчество), " " + СвойстваСубъекта.Отчество, "");
				ИмяИОтчествоСертификата = ?(ЗначениеЗаполнено(СертификатОбъект.Имя), СертификатОбъект.Имя, "")
					+ ?(ЗначениеЗаполнено(СертификатОбъект.Отчество), " " + СертификатОбъект.Отчество, "");
				Если ИмяИОтчествоСубъекта <> ИмяИОтчествоСертификата Тогда
					ОбновитьЗначение(СертификатОбъект.Имя, СвойстваСубъекта.Имя, Истина);
					ОбновитьЗначение(СертификатОбъект.Отчество, СвойстваСубъекта.Отчество, Истина);
				КонецЕсли;
			Иначе
				ОбновитьЗначение(СертификатОбъект.Имя, СвойстваСубъекта.Имя, Истина);
			КонецЕсли;
			
			ОбновитьЗначение(СертификатОбъект.Фамилия,   СвойстваСубъекта.Фамилия,     Истина);
			ОбновитьЗначение(СертификатОбъект.Фирма,     СвойстваСубъекта.Организация, Истина);
			
			Если СвойстваСубъекта.Свойство("Должность") Тогда
				ОбновитьЗначение(СертификатОбъект.Должность, СвойстваСубъекта.Должность,   Истина);
			КонецЕсли;
		КонецЕсли;
		
		Если СертификатОбъект.ЭтоНовый()
			И Не ДополнительныеПараметры.Свойство("Наименование") Тогда
		
			Если СвойстваСертификата = Неопределено Тогда
				СвойстваСертификата = СвойстваСертификата(СертификатКриптографии);
			КонецЕсли;
			
			ДополнительныеПараметры.Вставить("Наименование",
				СвойстваСертификата.Представление);
		КонецЕсли;
		
		Для Каждого КлючИЗначение Из ДополнительныеПараметры Цикл
			
			Если КлючИЗначение.Ключ = "СсылкаНаСертификат" Тогда
				Продолжить;
			КонецЕсли;
			
			Если РазрешенныеПоляДляИзменения <> Неопределено 
			   И РазрешенныеПоляДляИзменения.Найти(КлючИЗначение.Ключ) = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			
			Если КлючИЗначение.Ключ = "Пользователь" Тогда
				Если ЗначениеЗаполнено(КлючИЗначение.Значение) Тогда
					Если ЗначениеЗаполнено(СертификатОбъект.Пользователь) И СертификатОбъект.Пользователь <> КлючИЗначение.Значение Тогда
						ДобавитьПользователяВСертификат(СертификатОбъект.Пользователь, СертификатОбъект.Пользователи);
						ДобавитьПользователяВСертификат(КлючИЗначение.Значение, СертификатОбъект.Пользователи);
						СертификатОбъект.Пользователь = Неопределено;
					ИначеЕсли СертификатОбъект.Пользователи.Количество() > 0 Тогда
						ДобавитьПользователяВСертификат(КлючИЗначение.Значение, СертификатОбъект.Пользователи);
					Иначе
						ОбновитьЗначение(СертификатОбъект[КлючИЗначение.Ключ], КлючИЗначение.Значение);
					КонецЕсли;
				КонецЕсли;
			Иначе
				ОбновитьЗначение(СертификатОбъект[КлючИЗначение.Ключ], КлючИЗначение.Значение);
			КонецЕсли;
		КонецЦикла;
		
		Если СертификатОбъект.Модифицированность() Тогда
			СертификатОбъект.Записать();
		КонецЕсли;
		
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Возврат СертификатОбъект.Ссылка;
	
КонецФункции

// Возвращает табличный документ, содержащий штамп визуализации электронной подписи.
//
// Параметры:
//  Сертификат   - СертификатКриптографии - сертификат, которым подписан документ.
//  ДатаПодписи  - Дата - дата подписания документа.
//  ТекстОтметки - Строка - текст, выводящийся непосредственно под штампом и описывающий
//                          расположение подлинника документа.
//  ЛоготипОрганизации - Картинка - если не указан, то будет использована стандартная картинка.
//
// Возвращаемое значение:
//  ТабличныйДокумент - табличный документ, содержащий готовый штамп электронной подписи.
//
Функция ШтампВизуализацииЭлектроннойПодписи(Сертификат, ДатаПодписи = Неопределено, 
	ТекстОтметки = "", ЛоготипОрганизации = Неопределено) Экспорт
	
	Если ЛоготипОрганизации = Неопределено И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Организации") Тогда
		МодульОрганизацииСервер = ОбщегоНазначения.ОбщийМодуль("ОрганизацииСервер");
		СертификатСсылка = СсылкаНаСертификат(Сертификат);
		Если ЗначениеЗаполнено(СертификатСсылка) Тогда
			Организация = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СертификатСсылка, "Организация");
			Если ЗначениеЗаполнено(Организация) Тогда
				ДополнительныеСведения = МодульОрганизацииСервер.ДополнительныеСведенияОрганизации(
					Организация, , ДатаПодписи);
				Если ДополнительныеСведения.Свойство("ЭмблемаОрганизацииДляШтампаЭлектроннойПодписи") Тогда
					ЛоготипОрганизации = ДополнительныеСведения.ЭмблемаОрганизацииДляШтампаЭлектроннойПодписи;
				КонецЕсли;		
			КонецЕсли;	
		КонецЕсли;	
	КонецЕсли;	
	
	СвойстваСертификата = СвойстваСертификата(Сертификат);
	
	ПериодДействия = НСтр("ru = 'с %1 по %2'");
	ПериодДействия = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ПериодДействия,
		Формат(СвойстваСертификата.ДатаНачала,    "ДЛФ=D"),
		Формат(СвойстваСертификата.ДатаОкончания, "ДЛФ=D"));
	
	ПараметрыШтампа = Новый Структура;
	ПараметрыШтампа.Вставить("ДатаПодписи", ДатаПодписи);
	ПараметрыШтампа.Вставить("НомерСертификата", СвойстваСертификата.СерийныйНомер);
	ПараметрыШтампа.Вставить("ВыдалСертификат", СвойстваСертификата.КемВыдан);
	ПараметрыШтампа.Вставить("ВладелецСертификата", СвойстваСертификата.КомуВыдан);
	ПараметрыШтампа.Вставить("СрокДействия", ПериодДействия);
	ПараметрыШтампа.Вставить("ТекстОтметки", ТекстОтметки);
	
	Штамп = Справочники.СертификатыКлючейЭлектроннойПодписиИШифрования.ПолучитьМакет("Штамп");
	ЗаполнитьЗначенияСвойств(Штамп.Параметры, ПараметрыШтампа);
	Если ЛоготипОрганизации <> Неопределено Тогда
		Штамп.Области.Картинка.Картинка = ЛоготипОрганизации;
	КонецЕсли;
	
	Возврат Штамп;
	
КонецФункции

// Размещает штампы в переданном табличном документе.
//
// Параметры:
//  Документ        - ТабличныйДокумент - табличный документ, в который требуется добавить штампы.
//  ОписаниеШтампов - Массив - массив табличных документов, содержащих штампы, полученные
//                             функцией ЭлектроннаяПодпись.ШтампВизуализацииЭлектроннойПодписи.
//                             В данном случае переданные штампы будут выведены в конец документа,
//                             если в макете подписываемого табличного документа не определены области
//                             для размещения штампов, которые удовлетворяют следующим условиям:
//                               а) область вывода штампа размером две колонки и семь строк, с
//                                  произвольной шириной колонок;
//                               б) имя области задано как ШтампЭП + порядковый номер штампа,
//                                  например: ШтампЭП1 и т.д.
//                             В таком случае штампы будут выведены в заданные области в том
//                             порядке, в котором документ был подписан.
//                  - Соответствие из КлючИЗначение - описывает места вывода штампов, где:
//                       * Ключ     - Строка - имя области, куда выводить штамп. Для такой области должна
//                                    быть установлена произвольная ширина колонок, 
//                                    отличная от ширины колонок остального документа.
//                       * Значение - ТабличныйДокумент - штамп, полученный функцией
//                                       ЭлектроннаяПодпись.ШтампВизуализацииЭлектроннойПодписи.
//  Размеры         - Структура - позволяет изменять размеры штампа, со свойствами:
//                       * ЛеваяКолонка  - Число - ширина левой колонки штампа, содержащей заголовки свойств.
//                                                 По умолчанию - 10.
//                       * ПраваяКолонка - Число - ширина правой колонки штампа, содержащей значения свойств.
//                                                 По умолчанию - 30.
//
Процедура ДобавитьШтампыВТабличныйДокумент(Документ, ОписаниеШтампов, Размеры = Неопределено) Экспорт
	
	Если Размеры = Неопределено Тогда
		Размеры = Новый Структура;
		Размеры.Вставить("ЛеваяКолонка", 10);
		Размеры.Вставить("ПраваяКолонка", 30);
	КонецЕсли;
	
	Если ТипЗнч(ОписаниеШтампов) = Тип("Массив") Тогда
		
		ИндексШтампа = 1; КоличествоШтампов = ОписаниеШтампов.Количество();
		
		ШиринаКолонокШтампа = 3 + Размеры.ЛеваяКолонка + Размеры.ПраваяКолонка;
		
		КоличествоШтамповПоШирине = Неопределено;
		НомерШтампаВСтроке = 0; ШиринаСтрокиШтампов = 0; ШиринаТаблицы = 0;
		
		Для Каждого Штамп Из ОписаниеШтампов Цикл
			ИмяОбласти = "ШтампЭП" + Строка(ИндексШтампа);
			ОбластьНайдена = Документ.Области.Найти(ИмяОбласти) <> Неопределено;
			
			Если ОбластьНайдена Тогда
				Документ.ВставитьОбласть(Штамп.Области.Штамп, Документ.Области[ИмяОбласти],, Истина);
				Документ.Области.ШтампЛеваяКолонка.ШиринаКолонки  = Размеры.ЛеваяКолонка;
				Документ.Области.ШтампПраваяКолонка.ШиринаКолонки = Размеры.ПраваяКолонка;
			Иначе
				НомерШтампаВСтроке = НомерШтампаВСтроке + 1;
				
				ШиринаШтампа = Штамп.Области.Штамп.Право;
				ШтампВерх = Штамп.Области.Штамп.Верх;
				ШтампНиз = Штамп.Области.Штамп.Низ;
				ВысотаШтампа = ШтампНиз - ШтампВерх;
				
				Если НомерШтампаВСтроке = 1 Или НомерШтампаВСтроке > КоличествоШтамповПоШирине Тогда
					
					Если КоличествоШтамповПоШирине = Неопределено Тогда
						
						Для ШиринаТаблицы = 1 По Документ.ШиринаТаблицы Цикл
							
							Область = Документ.Область(, ШиринаТаблицы,, ШиринаТаблицы);
							ШиринаСтрокиШтампов = ШиринаСтрокиШтампов + Область.ШиринаКолонки;
							Если Область.КонецСтраницы Тогда
								Прервать;
							КонецЕсли;
							
							Если ШиринаТаблицы = Документ.ШиринаТаблицы Тогда
								
								ШиринаСтрокиШтампов = 0;
								ВысотаТаблицы = Мин(Документ.ВысотаТаблицы, 100);
								
								Для НомерКолонкиСКонца = 1 По ШиринаТаблицы Цикл
									
									НомерКолонки = ШиринаТаблицы - НомерКолонкиСКонца + 1;
									
									Для НомерСтрокиСКонца = 1 По ВысотаТаблицы Цикл
										
										НомерСтроки = Документ.ВысотаТаблицы - НомерСтрокиСКонца + 1;
										
										ОбластьЯчейки = Документ.Область(НомерСтроки, НомерКолонки);
										
										Если ПустаяСтрока(ОбластьЯчейки.Текст)
											И ОбластьЯчейки.Лево = НомерКолонки
											И ОбластьЯчейки.Верх = НомерСтроки
											И ОбластьЯчейки.ГраницаСправа.ТипЛинии = ТипЛинииЯчейкиТабличногоДокумента.НетЛинии
											И ОбластьЯчейки.ГраницаСнизу.ТипЛинии = ТипЛинииЯчейкиТабличногоДокумента.НетЛинии
											И ОбластьЯчейки.ГраницаСверху.ТипЛинии = ТипЛинииЯчейкиТабличногоДокумента.НетЛинии
											Тогда
											Продолжить;
										КонецЕсли;
										
										ТабличныйДокумент = Новый ТабличныйДокумент;
										Область = Документ.ПолучитьОбласть(НомерСтроки, 1, НомерСтроки, НомерКолонки);
										ТабличныйДокумент.Вывести(Область);
										
										Для НомерКолонкиТабличногоДокумента = 1 По НомерКолонки Цикл
											Область = ТабличныйДокумент.Область(1, НомерКолонкиТабличногоДокумента, 1,
												НомерКолонкиТабличногоДокумента);
											ШиринаСтрокиШтампов = ШиринаСтрокиШтампов + Область.ШиринаКолонки;
										КонецЦикла;
										
										Прервать;
									КонецЦикла;
									
									Если ШиринаСтрокиШтампов <> 0 Тогда
										Прервать;
									КонецЕсли;
								КонецЦикла;
								
								Прервать;
							КонецЕсли;
						КонецЦикла;
						КоличествоШтамповПоШирине = Макс(1, Цел(ШиринаСтрокиШтампов/ШиринаКолонокШтампа));
					КонецЕсли;
						
					ШиринаНачало = 1; НомерШтампаВСтроке = 1;
					
					ОбластиДляПроверкиПоВысоте = Новый Массив;
					ОбластиДляПроверкиПоВысоте.Добавить(Штамп.ПолучитьОбласть("Отступ"));
					ОбластиДляПроверкиПоВысоте.Добавить(Штамп.ПолучитьОбласть("ОбластьСтрокШтамп"));
					Если Не ОбщегоНазначения.ПроверитьВыводТабличногоДокумента(Документ, ОбластиДляПроверкиПоВысоте, Истина) Тогда
						Документ.ВывестиГоризонтальныйРазделительСтраниц();
					КонецЕсли;
					
					Документ.Вывести(Штамп.ПолучитьОбласть("Отступ"));
					
					ВысотаНачало = Документ.ВысотаТаблицы;
					ВысотаКонец = Документ.ВысотаТаблицы + ВысотаШтампа;
					
					Документ.Область(ВысотаНачало, ШиринаНачало, ВысотаКонец, ШиринаТаблицы).Разъединить();
					// Формат строк, чтобы изменение ширины колонок не повлияло на остальной документ.
					Документ.Область(ВысотаНачало, , ВысотаКонец).СоздатьФорматСтрок();
					
					ОстатокШирины = ШиринаСтрокиШтампов;
					
				КонецЕсли;
				
				ШиринаКонец = ШиринаНачало - 1 + ШиринаШтампа;
				
				// Вставка в разъединенные области область из макета штампа.
				ИсходнаяОбласть = Штамп.Область(ШтампВерх, 1, ШтампНиз, ШиринаШтампа);
				ОбластьПриемник = Документ.Область(ВысотаНачало, ШиринаНачало, ВысотаКонец, ШиринаКонец);
				Документ.ВставитьОбласть(ИсходнаяОбласть, ОбластьПриемник, , Истина);
				
				Документ.Области.ШтампЛеваяКолонка.ШиринаКолонки  = Размеры.ЛеваяКолонка;
				Документ.Области.ШтампПраваяКолонка.ШиринаКолонки = Размеры.ПраваяКолонка;
				Документ.Области.ОтступШтампа.ШиринаКолонки       = 3;
					
				ШиринаНачало = ШиринаКонец + 1;
				
				Если ОстатокШирины > ШиринаКолонокШтампа Или ИндексШтампа = КоличествоШтампов Тогда
					
					ОстатокШирины = ОстатокШирины - ШиринаКолонокШтампа;
					
					Если ОстатокШирины < ШиринаКолонокШтампа Или ИндексШтампа = КоличествоШтампов Тогда
						Если ШиринаНачало < ШиринаТаблицы Тогда
							Документ.Область(ВысотаНачало, ШиринаНачало, ВысотаКонец, ШиринаТаблицы).ШиринаКолонки = 0;
						КонецЕсли;
						Если ОстатокШирины > 0 Тогда
							Документ.Область(ВысотаНачало, ШиринаНачало, ВысотаКонец, ШиринаНачало).ШиринаКолонки = ОстатокШирины;
						КонецЕсли;
					КонецЕсли;
				КонецЕсли;
			КонецЕсли;
			
			ИндексШтампа = ИндексШтампа + 1;
		КонецЦикла;

	Иначе
		Для Каждого ОписаниеШтампа Из ОписаниеШтампов Цикл
			ИмяОбласти = ОписаниеШтампа.Ключ;
			Штамп      = ОписаниеШтампа.Значение;
			ОбластьНайдена = Документ.Области.Найти(ИмяОбласти) <> Неопределено;
			Если ОбластьНайдена Тогда
				Документ.ВставитьОбласть(Штамп.Области.Штамп, Документ.Области[ИмяОбласти],, Истина);
				Документ.Области.ШтампЛеваяКолонка.ШиринаКолонки  = Размеры.ЛеваяКолонка;
				Документ.Области.ШтампПраваяКолонка.ШиринаКолонки = Размеры.ПраваяКолонка;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// См. ЭлектроннаяПодписьКлиент.ПредставлениеСертификата.
Функция ПредставлениеСертификата(Сертификат) Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебныйКлиентСервер.ПредставлениеСертификата(Сертификат, ЭлектроннаяПодписьСлужебный.ДобавкаВремени());
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.ПредставлениеСубъекта.
Функция ПредставлениеСубъекта(Сертификат) Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебныйКлиентСервер.ПредставлениеСубъекта(Сертификат);
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.ПредставлениеИздателя.
Функция ПредставлениеИздателя(Сертификат) Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебныйКлиентСервер.ПредставлениеИздателя(Сертификат);
	
КонецФункции

// Возвращает основные свойства сертификата в виде структуры.
//
// Параметры:
//   Сертификат - СертификатКриптографии - сертификат криптографии.
//
// Возвращаемое значение:
//   Структура:
//    * Отпечаток      - Строка - отпечаток сертификата в формате строки Base64.
//    * СерийныйНомер  - ДвоичныеДанные - свойство сертификата СерийныйНомер.
//    * Представление  - см. ЭлектроннаяПодписьКлиент.ПредставлениеСертификата.
//    * КомуВыдан      - см. ЭлектроннаяПодписьКлиент.ПредставлениеСубъекта.
//    * КемВыдан       - см. ЭлектроннаяПодписьКлиент.ПредставлениеИздателя.
//    * ДатаНачала     - Дата   - свойство сертификата ДатаНачала в часовом поясе сеанса.
//    * ДатаОкончания  - Дата   - свойство сертификата ДатаОкончания в часовом поясе сеанса.
//    * ДатаНачалаЗакрытогоКлюча     - Дата   - свойство сертификата, указанное в OID 2.5.29.16 в часовом поясе сеанса.
//    * ДатаОкончанияЗакрытогоКлюча  - Дата   - свойство сертификата, указанное в OID 2.5.29.16 в часовом поясе сеанса.
//    * ДействителенДо - Дата - минимальная дата из ДатаОкончания и ДатаОкончанияЗакрытогоКлюча (если указана в сертификате).
//    * Назначение     - Строка - описание расширенного свойства сертификата EKU.
//    * Подписание     - Булево - свойство сертификата ИспользоватьДляПодписи.
//    * Шифрование     - Булево - свойство сертификата ИспользоватьДляШифрования.
//
Функция СвойстваСертификата(Сертификат) Экспорт
	
	Если ТипЗнч(Сертификат) = Тип("СертификатКриптографии") Тогда
		ДвоичныеДанныеСертификата = Сертификат.Выгрузить();
	ИначеЕсли ТипЗнч(Сертификат) = Тип("ФиксированнаяСтруктура") Тогда
		ДвоичныеДанныеСертификата = Сертификат.Сертификат;
	Иначе
		ДвоичныеДанныеСертификата = Неопределено;
	КонецЕсли;
	
	Возврат ЭлектроннаяПодписьСлужебныйКлиентСервер.СвойстваСертификата(
		Сертификат, ЭлектроннаяПодписьСлужебный.ДобавкаВремени(), ДвоичныеДанныеСертификата);
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.СвойстваСубъектаСертификата.
Функция СвойстваСубъектаСертификата(Сертификат) Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебныйКлиентСервер.СвойстваСубъектаСертификата(Сертификат);
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.СвойстваИздателяСертификата.
Функция СвойстваИздателяСертификата(Сертификат) Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебныйКлиентСервер.СвойстваИздателяСертификата(Сертификат);
	
КонецФункции

// Ищет текст ошибки в классификаторе типовых проблем при работе с электронной подписью и,
// если находит, возвращает причины ее возникновения и способы устранения.
//
// Параметры:
//   ТекстДляПоискаВКлассификаторе - Строка - текст, по которому осуществляется поиск в классификаторе.
//   ОшибкаНаСервере               - Булево - (по умолчанию Ложь) контекст возникновения ошибки (причины и решения
//                                   на клиенте и сервере могут отличаться).
//
// Возвращаемое значение:
//   Неопределено - ошибка не существует в классификаторе.
//   Структура:
//     * Причина          - Строка - возможные причины возникновения ошибки.
//     * Решение          - Строка - возможные способы решения возникшей ошибки.
//     * СпособУстранения - Строка - идентификатор способа автоматического устранения ошибки.
//     * Ссылка           - Строка - идентификатора якоря в статье на сайте ИТС.
//
Функция ОшибкаПоКлассификатору(ТекстДляПоискаВКлассификаторе, ОшибкаНаСервере = Ложь) Экспорт
	
	Результат = ЭлектроннаяПодписьСлужебный.ОшибкаПоКлассификатору(ТекстДляПоискаВКлассификаторе, ОшибкаНаСервере);
	
	Если Результат = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Структура = Новый Структура("Причина, Решение, СпособУстранения, Ссылка");
	ЗаполнитьЗначенияСвойств(Структура, Результат);
	
	Возврат Структура;
	
КонецФункции

// Усовершенствует подпись до переданного типа, если подпись позволяет.
// Добавляет архивную метку времени в архивную подпись (CAdES-A).
// 
// Параметры:
//  Подпись                      - ДвоичныеДанные - двоичные данные электронной подписи.
//  ТипПодписи                   - ПеречислениеСсылка.ТипыПодписиКриптографии - тип подписи,
//                                  до которого требуется усовершенствование. Если фактический ТипПодписи
//                                  тот же или выше, тогда никаких действий произведено не будет.
//  ДобавитьАрхивнуюМеткуВремени - Булево - если указано Истина и фактический ТипПодписи архивная,
//                                   тогда будет добавлена метка времени.
//  ДополнительныеПараметры - Структура:
//                             * МенеджерКриптографии - Неопределено - получить менеджер криптографии для проверки
//                                                    - МенеджерКриптографии - использовать указанный менеджер криптографии.
//                             * ИгнорироватьСрокДействияСертификата  - Булево - по умолчанию Ложь, не проверять 
//                                                      срок действия сертификата подписи перед усовершенствованием.
//                          - Неопределено - получить менеджер криптографии для проверки
//                                 электронных подписей, как настроил администратор.
//                          - МенеджерКриптографии - использовать указанный менеджер криптографии.
// 
// Возвращаемое значение:
//  Структура:
//   * Успех - Булево - Истина, если усовершенствование прошло успешно или не требовалось.
//   * ТекстОшибки - Строка - заполнен, если Успех = Ложь.
//   * СвойстваПодписи - см. ЭлектроннаяПодписьКлиентСервер.НовыеСвойстваПодписи.
//
Функция УсовершенствоватьПодпись(Подпись, ТипПодписи, ДобавитьАрхивнуюМеткуВремени = Ложь,
	ДополнительныеПараметры = Неопределено) Экспорт
	
	ПараметрыУсовершенствования = Новый Структура;
	ПараметрыУсовершенствования.Вставить("МенеджерКриптографии", Неопределено);
	ПараметрыУсовершенствования.Вставить("ИгнорироватьСрокДействияСертификата", Ложь);
	
	Если ТипЗнч(ДополнительныеПараметры) = Тип("Структура") Тогда
		ЗаполнитьЗначенияСвойств(ПараметрыУсовершенствования, ДополнительныеПараметры);
	Иначе
		ПараметрыУсовершенствования.МенеджерКриптографии = ДополнительныеПараметры;
	КонецЕсли;
	
	МенеджерКриптографии = ПараметрыУсовершенствования.МенеджерКриптографии;
	
	Результат = Новый Структура("Успех, ТекстОшибки, СвойстваПодписи", Ложь);

	Если МенеджерКриптографии = Неопределено Тогда
		
		ПараметрыСоздания = ЭлектроннаяПодписьСлужебный.ПараметрыСозданияМенеджераКриптографии();
		ПараметрыСоздания.АлгоритмПодписи =
			ЭлектроннаяПодписьСлужебныйКлиентСервер.АлгоритмСформированнойПодписи(Подпись);
		
		МенеджерКриптографии = ЭлектроннаяПодписьСлужебный.МенеджерКриптографии(
			"ПродлениеСрокаДействияПодписи", ПараметрыСоздания);
		
		Если МенеджерКриптографии = Неопределено Тогда
			Результат.ТекстОшибки = ПараметрыСоздания.ОписаниеОшибки;
			Возврат Результат;
		КонецЕсли;
		
		МенеджерКриптографии.АдресаСерверовМетокВремени = ОбщиеНастройки().АдресаСерверовМетокВремени;

	КонецЕсли;
	
	СвойстваПодписи = ЭлектроннаяПодписьКлиентСервер.НовыеСвойстваПодписи();
	
	Попытка
		КонтейнерПодписи = МенеджерКриптографии.ПолучитьКонтейнерПодписейКриптографии(Подпись);
	Исключение
		Результат.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru='Не удалось прочитать данные подписи: %1'"), ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
		Возврат Результат;
	КонецПопытки;
	
	ПараметрыПодписиКриптографии = ЭлектроннаяПодписьСлужебный.ПараметрыПодписиКриптографии(КонтейнерПодписи,
		ЭлектроннаяПодписьСлужебный.ДобавкаВремени(), ТекущаяДатаСеанса());
		
	Если ПараметрыПодписиКриптографии.СертификатПоследнейМеткиВремени = Неопределено Тогда
		Результат.ТекстОшибки = НСтр("ru='Не удалось получить сертификат подписи'");
		Возврат Результат;
	КонецЕсли;
	
	СвойстваПодписи.ТипПодписи = ПараметрыПодписиКриптографии.ТипПодписи;
	СвойстваПодписи.СрокДействияПоследнейМеткиВремени = ПараметрыПодписиКриптографии.СрокДействияПоследнейМеткиВремени; 
	
	Результат.СвойстваПодписи = СвойстваПодписи;
	
	Если Не ПараметрыУсовершенствования.ИгнорироватьСрокДействияСертификата
		И ЗначениеЗаполнено(ПараметрыПодписиКриптографии.СрокДействияПоследнейМеткиВремени)
		И ПараметрыПодписиКриптографии.СрокДействияПоследнейМеткиВремени < ТекущаяДатаСеанса() Тогда
		Результат.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru='Истек срок действия подписи: %1'"), ПараметрыПодписиКриптографии.СрокДействияПоследнейМеткиВремени);
		Возврат Результат;
	КонецЕсли;
	
	ОписаниеОшибки = "";
	РезультатПроверкиСертификата = ПроверитьСертификат(МенеджерКриптографии,
		ПараметрыПодписиКриптографии.СертификатПоследнейМеткиВремени, ОписаниеОшибки);
	Если РезультатПроверкиСертификата Тогда
		Если ЗначениеЗаполнено(ТипПодписи) И ЭлектроннаяПодписьСлужебныйКлиентСервер.ПодлежитУсовершенствованию(
			ПараметрыПодписиКриптографии.ТипПодписи, ТипПодписи) Тогда
			Попытка
				ДвоичныеДанныеРезультата = МенеджерКриптографии.УсовершенствоватьПодпись(Подпись,
					ЭлектроннаяПодписьСлужебныйКлиентСервер.ТипПодписиКриптографии(ТипПодписи));
			Исключение
				Результат.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru='Не удалось усовершенствовать подпись: %1'"), ОбработкаОшибок.КраткоеПредставлениеОшибки(
					ИнформацияОбОшибке()));
				Возврат Результат;
			КонецПопытки;
		ИначеЕсли ДобавитьАрхивнуюМеткуВремени И (ПараметрыПодписиКриптографии.ТипПодписи
			= Перечисления.ТипыПодписиКриптографии.АрхивнаяCAdESAv3 Или ПараметрыПодписиКриптографии.ТипПодписи
			= Перечисления.ТипыПодписиКриптографии.CAdESAv2) Тогда
			Попытка
				ДвоичныеДанныеРезультата = МенеджерКриптографии.ДобавитьАрхивнуюМеткуВремени(Подпись);
			Исключение
				Результат.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru='Не удалось добавить архивную метку времени в подпись: %1'"), ОбработкаОшибок.КраткоеПредставлениеОшибки(
					ИнформацияОбОшибке()));
				Возврат Результат;
			КонецПопытки;
		Иначе // Есть подписи, которые не нужно усовершенствовать, но нужно заполнить их свойства, они уже прочитаны выше.
			Результат.Успех = Истина;
			Возврат Результат;
		КонецЕсли;
	Иначе
		СвойстваСертификата = СвойстваСертификата(ПараметрыПодписиКриптографии.СертификатПоследнейМеткиВремени);
		СведенияОСертификате = ЭлектроннаяПодписьСлужебныйКлиентСервер.СведенияОСертификатеСтрокой(
				СвойстваСертификата);
				
		Результат.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru='Не действует сертификат подписи: %1
				|%2'"), ОписаниеОшибки, СведенияОСертификате);
		Возврат Результат;
	КонецЕсли;
	
	Попытка
		КонтейнерПодписи = МенеджерКриптографии.ПолучитьКонтейнерПодписейКриптографии(ДвоичныеДанныеРезультата);
	Исключение
		Результат.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru='Не удалось прочитать данные усовершенствованной подписи: %1'"), ОбработкаОшибок.КраткоеПредставлениеОшибки(
			ИнформацияОбОшибке()));
		Возврат Результат;
	КонецПопытки;

	ПараметрыПодписиКриптографии = ЭлектроннаяПодписьСлужебный.ПараметрыПодписиКриптографии(
			КонтейнерПодписи, ЭлектроннаяПодписьСлужебный.ДобавкаВремени(), ТекущаяДатаСеанса());
	
	РезультатПроверкиСертификата = ПроверитьСертификат(МенеджерКриптографии,
		ПараметрыПодписиКриптографии.СертификатПоследнейМеткиВремени, ОписаниеОшибки);

	Если РезультатПроверкиСертификата Тогда
		СвойстваПодписи.Подпись = ДвоичныеДанныеРезультата;
		СвойстваПодписи.ТипПодписи = ПараметрыПодписиКриптографии.ТипПодписи;
		СвойстваПодписи.СрокДействияПоследнейМеткиВремени = ПараметрыПодписиКриптографии.СрокДействияПоследнейМеткиВремени;
		Результат.СвойстваПодписи = СвойстваПодписи;
	Иначе
		СвойстваСертификата = СвойстваСертификата(ПараметрыПодписиКриптографии.СертификатПоследнейМеткиВремени);
		СведенияОСертификате = ЭлектроннаяПодписьСлужебныйКлиентСервер.СведенияОСертификатеСтрокой(
				СвойстваСертификата);

		ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru='Не действует сертификат полученной метки времени: %1
					 |%2'"), ОписаниеОшибки, СведенияОСертификате);

		ВызватьИсключение ОписаниеОшибки;
	КонецЕсли;
	
	Результат.Успех = Истина;
	Возврат Результат;
	
КонецФункции

// Усовершенствует подпись объекта до переданного типа, если подпись позволяет.
// Добавляет архивную метку времени в архивную подпись (CAdES-A).
// Обновляет данные подписи объекта (Тип подписи, Срок действия последней метки времени).
// 
// Параметры:
//  ПодписанныйОбъект - ОпределяемыйТип.ПодписанныйОбъект - ссылка по которой будет получена подпись для
//           усовершенствования и для блокировки при обновлении.
//
//  ПорядковыйНомер - Число - порядковый номер подписи.
//
//  ТипПодписи      - ПеречислениеСсылка.ТипыПодписиКриптографии - тип подписи,
//                    до которого требуется усовершенствование. Если фактический ТипПодписи
//                    тот же или выше, тогда никаких действий произведено не будет.
//
//  ДобавитьАрхивнуюМеткуВремени - Булево - если указано Истина и ТипПодписи указан архивная,
//                           и фактический ТипПодписи архивная, тогда будет добавлена метка времени.
//
//  ИдентификаторФормы - УникальныйИдентификатор - идентификатор формы, используемый для блокировки,
//                      если передана ссылка на объект.
//
//  ДополнительныеПараметры - Структура:
//                             * МенеджерКриптографии - Неопределено, МенеджерКриптографии - описание ниже.
//                             * ИгнорироватьСрокДействияСертификата  - Булево - по умолчанию Ложь, не проверять 
//                                                      срок действия сертификата подписи перед усовершенствованием.
//                          - Неопределено - получить менеджер криптографии для проверки
//                                 электронных подписей, как настроил администратор.
//                          - МенеджерКриптографии - использовать указанный менеджер криптографии.
//
// Возвращаемое значение:
//  Структура:
//   * Успех - Булево - Истина, если усовершенствование прошло успешно или не требовалось.
//   * ТекстОшибки - Строка - заполнен, если Успех = Ложь.
//   * СвойстваПодписи - см. ЭлектроннаяПодписьКлиентСервер.НовыеСвойстваПодписи
//   
Функция УсовершенствоватьПодписьОбъекта(ПодписанныйОбъект, ПорядковыйНомер, ТипПодписи, ДобавитьАрхивнуюМеткуВремени = Ложь,
			ИдентификаторФормы = Неопределено, ДополнительныеПараметры = Неопределено) Экспорт
			
			
	ПараметрыУсовершенствования = Новый Структура;
	ПараметрыУсовершенствования.Вставить("МенеджерКриптографии", Неопределено);
	ПараметрыУсовершенствования.Вставить("ИгнорироватьСрокДействияСертификата", Ложь);
	
	Если ТипЗнч(ДополнительныеПараметры) = Тип("Структура") Тогда
		ЗаполнитьЗначенияСвойств(ПараметрыУсовершенствования, ДополнительныеПараметры);
	Иначе
		ПараметрыУсовершенствования.МенеджерКриптографии = ДополнительныеПараметры;
	КонецЕсли;

	Результат = Новый Структура("Успех, ТекстОшибки, СвойстваПодписи", Ложь);
	
	УстановленныеПодписи = УстановленныеПодписи(ПодписанныйОбъект, ПорядковыйНомер);
	
	Если УстановленныеПодписи.Количество() = 0 Тогда
		Результат.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось найти подпись с порядковым номером %1 у %2'"), ПорядковыйНомер, ПодписанныйОбъект);
		Возврат Результат;
	КонецЕсли;
		
	СвойстваПодписи = УстановленныеПодписи[0];
	Подпись = СвойстваПодписи.Подпись;
	
	Результат = УсовершенствоватьПодпись(Подпись, ТипПодписи, ДобавитьАрхивнуюМеткуВремени, ПараметрыУсовершенствования);
		
	Если Результат.СвойстваПодписи = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	Результат.СвойстваПодписи.ПодписанныйОбъект = ПодписанныйОбъект;
	Результат.СвойстваПодписи.ПорядковыйНомер = ПорядковыйНомер;

	Если Результат.СвойстваПодписи.Подпись = Неопределено Тогда
		// Подпись не была усовершенствована, но, возможно, требуется обновление данных.
		Если СвойстваПодписи.ТипПодписи = Результат.СвойстваПодписи.ТипПодписи
			И СвойстваПодписи.СрокДействияПоследнейМеткиВремени = Результат.СвойстваПодписи.СрокДействияПоследнейМеткиВремени Тогда
			Возврат Результат;
		КонецЕсли;
	КонецЕсли;
	
	ПредставлениеОшибки = ЭлектроннаяПодписьСлужебный.ОбновитьУсовершенствованнуюПодпись(
		Результат.СвойстваПодписи);
	
	Если ЗначениеЗаполнено(ПредставлениеОшибки) Тогда
		Результат.ТекстОшибки = ПредставлениеОшибки;
		Результат.Успех = Ложь;
		Результат.СвойстваПодписи = Неопределено;
		Возврат Результат;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(Результат.СвойстваПодписи.Подпись) Тогда
		ЭлектроннаяПодписьСлужебный.ЗарегистрироватьУсовершенствованиеПодписиВЖурнале(Результат.СвойстваПодписи);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

#Область ДляВызоваИзДругихПодсистем

// Следующие процедуры и функции предназначены для интеграции с 1С:Библиотека электронных документов.

// Возвращает менеджер криптографии (на сервере) для указанной программы.
//
// Параметры:
//  Операция       - Строка - если не пустая, то должна содержать одну из строк, которые определяют
//                   операцию для вставки в описание ошибки: Подписание, ПроверкаПодписи, Шифрование,
//                   Расшифровка, ПроверкаСертификата, ПолучениеСертификатов.
//
//  ПоказатьОшибку - Булево - если Истина, тогда будет вызвано исключение, содержащее описание ошибки.
//
//  ОписаниеОшибки - Строка - возвращаемое описание ошибки, когда функция возвратила значение Неопределено.
//
//  Программа      - Неопределено - возвращает менеджер криптографии первой
//                   программы из справочника для которой удалось его создать.
//                 - СправочникСсылка.ПрограммыЭлектроннойПодписиИШифрования - программа
//                   для которой нужно создать и вернуть менеджер криптографии.
//                 - Структура - см. НовоеОписаниеПрограммы.
//                 - ДвоичныеДанные - данные подписи или сертификата в кодировке DER для определения программы.
//                 - Строка - адрес двоичных данных, описанных выше, во временном хранилище.
//
// Возвращаемое значение:
//   МенеджерКриптографии - менеджер криптографии.
//   Неопределено - произошла ошибка, описание которой в параметре ОписаниеОшибки.
//
Функция МенеджерКриптографии(Операция, ПоказатьОшибку = Истина, ОписаниеОшибки = "", Программа = Неопределено) Экспорт
	
	ПараметрыСоздания = ЭлектроннаяПодписьСлужебный.ПараметрыСозданияМенеджераКриптографии();
	ПараметрыСоздания.Программа = Программа;
	ПараметрыСоздания.ПоказатьОшибку = ПоказатьОшибку;
	
	Результат = ЭлектроннаяПодписьСлужебный.МенеджерКриптографии(Операция, ПараметрыСоздания);
	
	Если Результат = Неопределено Тогда
		ОписаниеОшибки = ПараметрыСоздания.ОписаниеОшибки;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Извлекает свойства подписи из данных подписи. Если не удалось извлечь все свойства с помощью менеджера криптографии, 
// возвращается часть свойств, которые удалось прочитать из двоичных данных.
//
// Параметры:
//   Подпись - ДвоичныеДанные - данные подписи в кодировке DER.
//   ПрочитатьСертификаты - Булево - (по умолчанию Истина), если Ложь, свойства Сертификат, Отпечаток,
//                                   КомуВыданСертификат, Сертификаты не будут заполнены.
//
// Возвращаемое значение:
//   Структура:
//       * Успех       - Булево, Неопределено - Неопределено, если не удалось прочитать все свойства с помощью менеджера
//                                              криптографии.
//       * ТекстОшибки - Строка - заполнена, если Успех = Ложь или Неопределено
//       * ТипПодписи  - ПеречислениеСсылка.ТипыПодписиКриптографии
//       * СрокДействияПоследнейМеткиВремени - Дата, Неопределено - заполняется только с помощью менеджера криптографии.
//       * ДатаПодписиИзМетки - Дата, Неопределено - самый ранний штамп времени: CADES-T, если его нет, но есть другие штампы,
//               дата заполняется только с помощью менеджера криптографии.
//       * НеподтвержденнаяДатаПодписи - Дата - неподтвержденная дата подписи.
//                                     - Неопределено - неподтвержденная дата подписи отсутствует в данных подписи.
//       * Сертификат  - ДвоичныеДанные - сертификат для проверки подписи.
//       * Отпечаток           - Строка - отпечаток сертификата в формате строки Base64.
//       * КомуВыданСертификат - Строка - представление субъекта, полученное из двоичных данных сертификата. 
//       * Сертификаты - Массив из ДвоичныеДанные - сертификаты для проверки подписи.
//
Функция СвойстваПодписи(Подпись, ПрочитатьСертификаты = Истина) Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебный.СвойстваПодписи(Подпись, ПрочитатьСертификаты)
	
КонецФункции

// Выполняет проверку установленных программ. Возвращает только установленные программы.
// 
// Параметры:
//  ПараметрыПроверки - Неопределено, Структура:
//   * ПроверяемыеПрограммы - Неопределено - (по умолчанию) возвращаются все установленные программы.
//                          - Булево - см. ЭлектроннаяПодписьСлужебныйКлиентСервер.АктуальныеАлгоритмыПрограмм
//                          - ДвоичныеДанные - данные подписи или сертификата, для проверки наличия подходящих программ.
//                          - Строка - адрес во временном хранилище данных подписи или сертификата,
//                               для проверки наличия подходящих программ.
//                          - Массив - со значениями, как возвращает функция ЭлектроннаяПодпись.НовоеОписаниеПрограммы.
//  Возвращаемое значение:
//    Структура:
//     * ПроверкаВыполнена - Булево - Истина, если проверка выполнена на компьютере, получены установленные криптопровайдеры.
//                 если Ложь, то будет заполнена Ошибка.
//     * Ошибка - Строка - текст ошибки.
//     * Программы - Массив из Структура:
//        ** ИмяПрограммы  - Строка  - имя криптопровайдера, например "Infotecs GOST 2012/512 Cryptographic Service Provider"
//        ** ТипПрограммы  - Число  - тип криптопровайдера, например, 77
//        ** Имя           - Строка  - представление программы для установки, из списка поставляемых,
//             например НСтр("ru = 'ViPNet CSP'")
//        ** Версия        - Строка - версия библиотеки
//        ** Лицензия      - Булево - есть лицензия
//     * ВозможенКонфликт - Булево - установлено несколько программ криптографии, между
//             которыми возможен конфликт.
//
Функция ПроверитьУстановкуПрограммКриптографии(ПараметрыПроверки = Неопределено) Экспорт
	
	Контекст = Новый Структура;
	Контекст.Вставить("ПроверяемыеПрограммы", Неопределено);
	Контекст.Вставить("РасширенноеОписание", Ложь);
	Контекст.Вставить("АлгоритмыПодписи", Новый Массив);
	Контекст.Вставить("ТипДанных", Неопределено);
	Контекст.Вставить("ЭтоСервер", Истина);
	
	Если ТипЗнч(ПараметрыПроверки) = Тип("Структура") Тогда
		ЗаполнитьЗначенияСвойств(Контекст, ПараметрыПроверки);
	КонецЕсли;
	
	Если Контекст.ПроверяемыеПрограммы <> Неопределено Тогда
		Если Контекст.ПроверяемыеПрограммы = Истина Тогда
			Контекст.ПроверяемыеПрограммы = Неопределено;
			Контекст.АлгоритмыПодписи = ЭлектроннаяПодписьСлужебныйКлиентСервер.АктуальныеАлгоритмыПрограмм();
			Контекст.ТипДанных = "Сертификат";
		ИначеЕсли ТипЗнч(Контекст.ПроверяемыеПрограммы) = Тип("ДвоичныеДанные")
			Или ТипЗнч(Контекст.ПроверяемыеПрограммы) = Тип("Строка") Тогда
			ДвоичныеДанные = ЭлектроннаяПодписьСлужебныйКлиентСервер.ДвоичныеДанныеИзДанных(
				Контекст.ПроверяемыеПрограммы, "ЭлектроннаяПодпись.ПроверитьУстановкуПрограммКриптографии");
			Контекст.ПроверяемыеПрограммы = Неопределено;
			Контекст.ТипДанных = ЭлектроннаяПодписьСлужебныйКлиентСервер.ОпределитьТипДанных(ДвоичныеДанные);
			Если Контекст.ТипДанных = "Сертификат" Тогда
				АлгоритмПодписи = ЭлектроннаяПодписьСлужебныйКлиентСервер.АлгоритмПодписиСертификата(ДвоичныеДанные);
			ИначеЕсли Контекст.ТипДанных = "Подпись" Тогда
				АлгоритмПодписи = ЭлектроннаяПодписьСлужебныйКлиентСервер.АлгоритмСформированнойПодписи(ДвоичныеДанные);
			Иначе
				ВызватьИсключение НСтр("ru = 'Данные для поиска программы криптографии не являются сертификатом или подписью.'");
			КонецЕсли;
			Контекст.АлгоритмыПодписи.Добавить(АлгоритмПодписи);
		КонецЕсли;
	КонецЕсли;
	
	Результат = ЭлектроннаяПодписьСлужебныйПовтИсп.УстановленныеКриптопровайдеры();
	
	РезультатПроверки = Новый Структура("Ошибка, ПроверкаВыполнена");
	РезультатПроверки.Вставить("Программы", Новый Массив);
	РезультатПроверки.Вставить("ВозможенКонфликт", Ложь);
	ЗаполнитьЗначенияСвойств(РезультатПроверки, Результат);
	Если Не РезультатПроверки.ПроверкаВыполнена Тогда
		Возврат РезультатПроверки;
	КонецЕсли;
	
	ЭлектроннаяПодписьСлужебныйКлиентСервер.ОбработатьРезультатПроверкиПрограмм(Результат.Криптопровайдеры,
		РезультатПроверки.Программы, РезультатПроверки.ВозможенКонфликт, Контекст);
		
	Возврат РезультатПроверки;
	
КонецФункции

// Проверяет действительность подписи и сертификата.
// Только для работы через средства платформы (МенеджерКриптографии).
//
// Параметры:
//   МенеджерКриптографии - Неопределено - получить менеджер криптографии для проверки
//                          электронных подписей, как настроил администратор.
//                        - МенеджерКриптографии - использовать указанный менеджер криптографии.
//
//   ИсходныеДанные       - ДвоичныеДанные - двоичные данные, которые были подписаны.
//                        - Строка         - адрес временного хранилища, содержащего двоичные данные.
//                        - Строка         - полное имя файла, содержащего двоичные данные,
//                                           которые были подписаны.
//                        - Структура:
//                           * КонвертXML       - Строка - подписанный КонвертXML,
//                                                         смотри также функцию КонвертXML.
//                           * ПараметрыXMLDSig - см. ЭлектроннаяПодпись.ПараметрыXMLDSig
//                        - Структура:
//                           * ПараметрыCMS - см. ЭлектроннаяПодпись.ПараметрыCMS
//                           * Данные  - Строка - произвольная строка для подписания,
//                                     - ДвоичныеДанные - двоичные данные для подписания.
//
//   Подпись              - ДвоичныеДанные - двоичные данные электронной подписи.
//                        - Строка         - адрес временного хранилища, содержащего двоичные данные.
//                        - Строка         - полное имя файла, содержащего двоичные данные
//                                           электронной подписи.
//                        - Неопределено   - если ИсходныеДанные конверт SOAP.
//
//   ОписаниеОшибки       - Null - вызвать исключение при ошибке проверки.
//                        - Строка - содержит описание ошибки, если произошла ошибка.
// 
//   НаДату               - Дата - проверить сертификат на указанную дату,
//                          если дату не удалось извлечь из подписи или если проверяется КонвертXML.
//                          Если параметр не заполнен, тогда проверять на текущую дату сеанса,
//                          если дату не удалось извлечь из подписи или если проверяется КонвертXML.
//   РезультатСтруктура   - см. ЭлектроннаяПодписьКлиентСервер.РезультатПроверкиПодписи.
//
// Возвращаемое значение:
//  Булево - Истина, если проверка выполнена успешно.
//           Ложь,   если не удалось получить менеджер криптографии (когда не указан),
//                   или произошла ошибка указанная в параметре ОписаниеОшибки.
//
Функция ПроверитьПодпись(МенеджерКриптографии, ИсходныеДанные, Подпись, ОписаниеОшибки = Null, НаДату = Неопределено, РезультатСтруктура = Неопределено) Экспорт

	РезультатПроверки = Ложь;
	
	ВызыватьИсключение = ОписаниеОшибки = Null;
	
	ИсходныеДанныеДляПроверки = ИсходныеДанные;
	Если ТипЗнч(ИсходныеДанные) = Тип("Строка") И ЭтоАдресВременногоХранилища(ИсходныеДанные) Тогда
		ИсходныеДанныеДляПроверки = ПолучитьИзВременногоХранилища(ИсходныеДанные);
	КонецЕсли;
	
	ЭтоXMLDSig = ТипЗнч(ИсходныеДанныеДляПроверки) = Тип("Структура")
		И ИсходныеДанныеДляПроверки.Свойство("ПараметрыXMLDSig");
	
	Если ЭтоXMLDSig Тогда
		Если Не ИсходныеДанныеДляПроверки.Свойство("КонвертXML") Тогда
			ИсходныеДанныеДляПроверки = Новый Структура(Новый ФиксированнаяСтруктура(ИсходныеДанныеДляПроверки));
			ИсходныеДанныеДляПроверки.Вставить("КонвертXML", ИсходныеДанныеДляПроверки.КонвертSOAP);
		КонецЕсли;
		СвойстваКонвертаXML = ЭлектроннаяПодписьСлужебный.СвойстваКонвертаXML(
			ИсходныеДанныеДляПроверки.КонвертXML, ИсходныеДанныеДляПроверки.ПараметрыXMLDSig, Истина);
		Если СвойстваКонвертаXML <> Неопределено
		   И ЗначениеЗаполнено(СвойстваКонвертаXML.ТекстОшибки) Тогда
			ОписаниеОшибки = СвойстваКонвертаXML.ТекстОшибки;
			ЗаполнитьРезультатПроверкиПодписи(ОписаниеОшибки, РезультатСтруктура);
			Если ВызыватьИсключение Тогда
				ВызватьИсключение ОписаниеОшибки;
			КонецЕсли;
			Возврат РезультатПроверки;
		КонецЕсли;
	КонецЕсли;
	
	ЭтоCMS = ТипЗнч(ИсходныеДанныеДляПроверки) = Тип("Структура")
		И ИсходныеДанныеДляПроверки.Свойство("ПараметрыCMS");
	
	Если ТипЗнч(Подпись) = Тип("Строка") И ЭтоАдресВременногоХранилища(Подпись) Тогда
		ПодписьДляПроверки = ПолучитьИзВременногоХранилища(Подпись);
	Иначе
		ПодписьДляПроверки = Подпись;
	КонецЕсли;
	
	МенеджерКриптографииДляПроверки = МенеджерКриптографии;
	
	Если МенеджерКриптографииДляПроверки = Неопределено Тогда
		ИспользоватьЭлектроннуюПодписьВМоделиСервиса = Не ЭтоXMLDSig И Не ЭтоCMS
			И ЭлектроннаяПодписьСлужебный.ИспользоватьЭлектроннуюПодписьВМоделиСервиса();
		
		ПараметрыСоздания = ЭлектроннаяПодписьСлужебный.ПараметрыСозданияМенеджераКриптографии();
		ПараметрыСоздания.ПоказатьОшибку = ВызыватьИсключение И Не ИспользоватьЭлектроннуюПодписьВМоделиСервиса;
		Если ТипЗнч(ПодписьДляПроверки) = Тип("ДвоичныеДанные") Тогда
			ПараметрыСоздания.АлгоритмПодписи =
				ЭлектроннаяПодписьСлужебныйКлиентСервер.АлгоритмСформированнойПодписи(ПодписьДляПроверки);
		ИначеЕсли ЭтоXMLDSig Тогда
			Если СвойстваКонвертаXML = Неопределено Тогда
				ДанныеСертификата = ЭлектроннаяПодписьСлужебныйКлиентСервер.СертификатИзКонвертаSOAP(
					ИсходныеДанныеДляПроверки.КонвертXML, Ложь);
			Иначе
				ДанныеСертификата = Base64Значение(СвойстваКонвертаXML.Сертификат.ЗначениеСертификата);
			КонецЕсли;
			
			ПараметрыСоздания.АлгоритмПодписи =
				ЭлектроннаяПодписьСлужебныйКлиентСервер.АлгоритмПодписиСертификата(ДанныеСертификата);
		КонецЕсли;
		
		МенеджерКриптографииДляПроверки = ЭлектроннаяПодписьСлужебный.МенеджерКриптографии(
			"ПроверкаПодписи", ПараметрыСоздания);
		
		Если МенеджерКриптографииДляПроверки = Неопределено Тогда
			ОписаниеОшибки = ПараметрыСоздания.ОписаниеОшибки;
			Если Не ИспользоватьЭлектроннуюПодписьВМоделиСервиса Тогда
				Если РезультатСтруктура <> Неопределено Тогда
					ЗаполнитьРезультатПроверкиПодписи(ОписаниеОшибки, РезультатСтруктура, Истина);
				КонецЕсли;
				Если ВызыватьИсключение Тогда
					ВызватьИсключение ОписаниеОшибки;
				КонецЕсли;
				Возврат РезультатПроверки;
			КонецЕсли;
		КонецЕсли;
		
	КонецЕсли;
	
	Если ЭтоXMLDSig Тогда
		Попытка
			Результат = ЭлектроннаяПодписьСлужебный.ПроверитьПодпись(
				ИсходныеДанныеДляПроверки.КонвертXML,
				ИсходныеДанныеДляПроверки.ПараметрыXMLDSig,
				МенеджерКриптографииДляПроверки,
				СвойстваКонвертаXML);
		Исключение
			ОписаниеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
			ЗаполнитьРезультатПроверкиПодписи(ОписаниеОшибки, РезультатСтруктура);
			Если ВызыватьИсключение Тогда
				ВызватьИсключение;
			КонецЕсли;
			Возврат РезультатПроверки;
		КонецПопытки;
		Сертификат     = Результат.Сертификат;
		ДатаДляПроверкиСертификатаПодписи = Результат.ДатаПодписания;
		
		Если РезультатСтруктура <> Неопределено Тогда
			РезультатСтруктура.НеподтвержденнаяДатаПодписи = ДатаДляПроверкиСертификатаПодписи;
			СвойстваСертификата = СвойстваСертификата(Сертификат);
			РезультатСтруктура.Сертификат = Сертификат.Выгрузить();
			РезультатСтруктура.Отпечаток = СвойстваСертификата.Отпечаток;
			РезультатСтруктура.КомуВыданСертификат = СвойстваСертификата.КомуВыдан;
		КонецЕсли;
		
	ИначеЕсли ЭтоCMS Тогда
		Попытка
			Результат = ЭлектроннаяПодписьСлужебный.ПроверитьПодписьCMS(
				ПодписьДляПроверки,
				ИсходныеДанныеДляПроверки.Данные,
				ИсходныеДанныеДляПроверки.ПараметрыCMS,
				МенеджерКриптографииДляПроверки);
		Исключение
			ОписаниеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
			ЗаполнитьРезультатПроверкиПодписи(ОписаниеОшибки, РезультатСтруктура);
			Если ВызыватьИсключение Тогда
				ВызватьИсключение;
			КонецЕсли;
			Возврат РезультатПроверки;
		КонецПопытки;
		
		Сертификат     = Результат.Сертификат;
		ДатаДляПроверкиСертификатаПодписи = Результат.ДатаПодписания;
		
		Если РезультатСтруктура <> Неопределено Тогда
			СвойстваПодписи = ЭлектроннаяПодписьСлужебный.СвойстваПодписиИзДвоичныхДанных(ПодписьДляПроверки, Ложь);
			СвойстваСертификата = СвойстваСертификата(Сертификат);
			СвойстваПодписи.Сертификат = Сертификат.Выгрузить();
			СвойстваПодписи.Отпечаток = СвойстваСертификата.Отпечаток;
			СвойстваПодписи.КомуВыданСертификат = СвойстваСертификата.КомуВыдан;
			ЗаполнитьЗначенияСвойств(РезультатСтруктура, СвойстваПодписи);
		КонецЕсли;
		
	ИначеЕсли МенеджерКриптографииДляПроверки = Неопределено
	      Или МенеджерКриптографииДляПроверки = "СервисКриптографии" Тогда
		
		МодульСервисКриптографии = ОбщегоНазначения.ОбщийМодуль("СервисКриптографии");
		
		Попытка
			Результат = МодульСервисКриптографии.ПроверитьПодпись(ПодписьДляПроверки, ИсходныеДанныеДляПроверки);
		Исключение
			ОписаниеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
			ЗаполнитьРезультатПроверкиПодписи(ОписаниеОшибки, РезультатСтруктура, Истина);
			Если ВызыватьИсключение Тогда
				ВызватьИсключение;
			КонецЕсли;
			Возврат РезультатПроверки;
		КонецПопытки;
		
		СвойстваПодписи = ЭлектроннаяПодписьСлужебный.СвойстваПодписиИзДвоичныхДанных(ПодписьДляПроверки, Истина);
		Если РезультатСтруктура <> Неопределено Тогда
			ЗаполнитьЗначенияСвойств(РезультатСтруктура, СвойстваПодписи);
		КонецЕсли;
		
		Если Не СвойстваПодписи.Успех Тогда
			ОписаниеОшибки = СвойстваПодписи.ТекстОшибки;
			ЗаполнитьРезультатПроверкиПодписи(ОписаниеОшибки, РезультатСтруктура);
			Если ВызыватьИсключение Тогда
				ВызватьИсключение ОписаниеОшибки;
			КонецЕсли;
			Возврат РезультатПроверки;
		КонецЕсли;
		
		Если СвойстваПодписи.Сертификат = Неопределено Тогда
			ОписаниеОшибки = НСтр("ru = 'Сертификат не существует в данных подписи.'");
			ЗаполнитьРезультатПроверкиПодписи(ОписаниеОшибки, РезультатСтруктура);
			Если ВызыватьИсключение Тогда
				ВызватьИсключение ОписаниеОшибки;
			КонецЕсли;
			Возврат РезультатПроверки;
		КонецЕсли;
		
		Если Не Результат Тогда
			ОписаниеОшибки = ЭлектроннаяПодписьСлужебныйКлиентСервер.ТекстОшибкиСервисаПодписьНедействительна();
			ЗаполнитьРезультатПроверкиПодписи(ОписаниеОшибки, РезультатСтруктура);
			Если ВызыватьИсключение Тогда
				ВызватьИсключение ОписаниеОшибки;
			КонецЕсли;
			Возврат РезультатПроверки;
		КонецЕсли;
		
		Если РезультатСтруктура <> Неопределено Тогда
			ЗаполнитьЗначенияСвойств(РезультатСтруктура, СвойстваПодписи);
		КонецЕсли;
		
		Сертификат = СвойстваПодписи.Сертификат;
		
		ДатаДляПроверкиСертификатаПодписи = ЭлектроннаяПодписьСлужебныйКлиентСервер.ДатаДляПроверкиСертификатаПодписи(СвойстваПодписи);
		Если Не ЗначениеЗаполнено(ДатаДляПроверкиСертификатаПодписи) Тогда
			ДатаДляПроверкиСертификатаПодписи = НаДату;
		КонецЕсли;
	Иначе
		Сертификат = Неопределено;
		
		ОшибкаПроверкиПодписи = "";
		Попытка
			МенеджерКриптографииДляПроверки.ПроверитьПодпись(ИсходныеДанныеДляПроверки, ПодписьДляПроверки, Сертификат);
		Исключение
			ОшибкаПроверкиПодписи = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
		КонецПопытки;
		
		Если ОбщиеНастройки().ДоступнаУсовершенствованнаяПодпись Тогда
			СвойстваПодписи = ЭлектроннаяПодписьСлужебный.СвойстваПодписиЧтениеМенеджеромКриптографии(
				ПодписьДляПроверки, МенеджерКриптографииДляПроверки, Ложь);
		Иначе
			СвойстваПодписи = ЭлектроннаяПодписьСлужебный.СвойстваПодписиИзДвоичныхДанных(ПодписьДляПроверки, Ложь);
		КонецЕсли;
		
		Если РезультатСтруктура <> Неопределено Тогда
			ЗаполнитьЗначенияСвойств(РезультатСтруктура, СвойстваПодписи);
			Если ЭлектроннаяПодписьСлужебныйКлиентСервер.СертификатСуществует(Сертификат) Тогда
				СвойстваСертификата = СвойстваСертификата(Сертификат);
				РезультатСтруктура.Сертификат = Сертификат.Выгрузить();
				РезультатСтруктура.Отпечаток = СвойстваСертификата.Отпечаток;
				РезультатСтруктура.КомуВыданСертификат = СвойстваСертификата.КомуВыдан;
			КонецЕсли;
		КонецЕсли;
		
		Если Не ПустаяСтрока(ОшибкаПроверкиПодписи) Тогда
			ОписаниеОшибки = ОшибкаПроверкиПодписи;
			ЗаполнитьРезультатПроверкиПодписи(ОписаниеОшибки, РезультатСтруктура, ТребуетсяПроверкаПодписи(ОписаниеОшибки, РезультатСтруктура));
			Если ВызыватьИсключение Тогда
				ВызватьИсключение ОписаниеОшибки;
			КонецЕсли;
			Возврат РезультатПроверки;
		КонецЕсли;
		
		ДатаДляПроверкиСертификатаПодписи = ЭлектроннаяПодписьСлужебныйКлиентСервер.ДатаДляПроверкиСертификатаПодписи(СвойстваПодписи);
		Если Не ЗначениеЗаполнено(ДатаДляПроверкиСертификатаПодписи) Тогда
			ДатаДляПроверкиСертификатаПодписи = НаДату;
		КонецЕсли;
		
	КонецЕсли;
	
	Если ВызыватьИсключение Тогда
		ОписаниеОшибки = Null;
	КонецЕсли;
	
	ДополнительныеПараметры = ЭлектроннаяПодписьСлужебный.ДополнительныеПараметрыПроверкиСертификата();
	ДополнительныеПараметры.ДляПроверкиПодписи = Истина;
		
	РезультатПроверкиСертификата = ЭлектроннаяПодписьСлужебный.ПроверитьСертификат(
		МенеджерКриптографииДляПроверки, Сертификат, ОписаниеОшибки, ДатаДляПроверкиСертификатаПодписи, ДополнительныеПараметры);
	
	Если РезультатПроверкиСертификата = Истина Тогда
		ЗаполнитьРезультатПроверкиПодписи(РезультатПроверкиСертификата, РезультатСтруктура);
	Иначе
		СертификатОтозван = СертификатПодписиОтозван(ОписаниеОшибки, РезультатСтруктура);
		Если Не СертификатОтозван Тогда
			ЗаполнитьРезультатПроверкиПодписи(ОписаниеОшибки, РезультатСтруктура, ТребуетсяПроверкаПодписи(
				ОписаниеОшибки, РезультатСтруктура));
		Иначе
			ЗаполнитьРезультатПроверкиПодписи(РезультатПроверкиСертификата, РезультатСтруктура, Ложь, Истина);
		КонецЕсли;
	КонецЕсли;
	
	Возврат РезультатПроверкиСертификата;
	
КонецФункции

// Проверяет действительность сертификата криптографии.
// Только для работы через средства платформы (МенеджерКриптографии).
//
// Параметры:
//   МенеджерКриптографии - Неопределено - получить менеджер криптографии автоматически.
//                        - МенеджерКриптографии - использовать указанный менеджер криптографии.
//
//   Сертификат           - СертификатКриптографии - сертификат.
//                        - ДвоичныеДанные - двоичные данные сертификата.
//                        - Строка - адрес временного хранилища, содержащего двоичные данные сертификата.
//
//   ОписаниеОшибки       - Null - вызвать исключение при ошибке проверки.
//                        - Строка - содержит описание ошибки, если произошла ошибка.
//
//   НаДату               - Дата - проверить сертификат на указанную дату.
//                          Если параметр не указан или указана пустая дата,
//                          тогда проверять на текущую дату сеанса.
//
// Возвращаемое значение:
//  Булево - Истина, если проверка выполнена успешно,
//           Ложь, если не удалось получить менеджер криптографии (когда не указан).
//
Функция ПроверитьСертификат(МенеджерКриптографии, Сертификат, ОписаниеОшибки = Null, НаДату = Неопределено) Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебный.ПроверитьСертификат(МенеджерКриптографии, Сертификат, ОписаниеОшибки, НаДату);
	
КонецФункции

// Находит сертификат на компьютере по строке отпечатка.
// Только для работы через средства платформы (МенеджерКриптографии).
//
// Параметры:
//   Отпечаток              - Строка - Base64 кодированный отпечаток сертификата.
//   ТолькоВЛичномХранилище - Булево - если Истина, тогда искать в личном хранилище, иначе везде.
//
// Возвращаемое значение:
//   СертификатКриптографии - сертификат электронной подписи и шифрования.
//   Неопределено - сертификат не существует в хранилище.
//
Функция ПолучитьСертификатПоОтпечатку(Отпечаток, ТолькоВЛичномХранилище) Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебный.ПолучитьСертификатПоОтпечатку(Отпечаток, ТолькоВЛичномХранилище);
	
КонецФункции

// Позволяет заполнить справочник ПрограммыЭлектроннойПодписиИШифрования, например, при обновлении ИБ.
// Только для работы через средства платформы (МенеджерКриптографии).
//
// Дополняет стандартный список из двух программ: ViPNet и КриптоПро.
// Если программа с указанными именем и типом уже существует, то ее свойства перезаполняются
// указанными. При заполнении не проверяется корректность указанных свойств.
//
// При заполнении можно использовать поставляемые описания программ,
// список которых находится в процедуре ПоставляемыеНастройкиПрограмм
// модуля менеджера справочника ПрограммыЭлектроннойПодписиИШифрования.
//
// Параметры:
//  ОписаниеПрограмм - Массив - содержит значения типа см. ЭлектроннаяПодпись.НовоеОписаниеПрограммы.
//                              Свойства структуры:
//   * ИмяПрограммы  - Строка - уникальное имя программы, присвоенное ее разработчиком,
//                       например, "Signal-COM CPGOST Cryptographic Provider".
//   * ТипПрограммы  - Число - специальное число, которое описывает тип программы и
//                       дополняет имя программы, например, 75.
//
//   Следующие параметры требуются, если указано Имя и Тип программы,
//   описание которой не поставляется, либо требуется обновить отдельные свойства.
//
//   * Представление       - Строка - наименование программы, которое увидит пользователь,
//                             например, "Сигнал-КОМ CSP (RFC 4357)".
//   * АлгоритмПодписи     - Строка - имя алгоритма подписи, который поддерживает
//                             указанная программа, например, "ECR3410-CP".
//   * АлгоритмХеширования - Строка - имя алгоритма хеширования данных, который поддерживает
//                             указанная программа, например, "RUS-HASH-CP". Используется для подготовки
//                             данных при формировании подписи с помощью алгоритма подписания.
//   * АлгоритмШифрования  - Строка - имя алгоритма шифрования, который поддерживает
//                             указанная программа, например, "GOST28147".
//
// Пример:
//	ОписаниеПрограмм = Новый Массив;
//	
//	// Заполнение дополнительной программы "Сигнал-КОМ CSP (RFC 4357)".
//	ОписаниеПрограммы = ЭлектроннаяПодпись.НовоеОписаниеПрограммы();
//	ОписаниеПрограммы.ИмяПрограммы = "Signal-COM CPGOST Cryptographic Provider";
//	ОписаниеПрограммы.ТипПрограммы = 75;
//	ОписаниеПрограмм.Добавить(ОписаниеПрограммы);
//	
//	// Изменение алгоритма поставляемой программы ViPNet CSP.
//	ОписаниеПрограммы = ЭлектроннаяПодпись.НовоеОписаниеПрограммы();
//	ОписаниеПрограммы.ИмяПрограммы = "Infotecs Cryptographic Service Provider";
//	ОписаниеПрограммы.ТипПрограммы = 2;
//	ОписаниеПрограммы.АлгоритмПодписи = "GOST R 34.10-2001";
//	ОписаниеПрограмм.Добавить(ОписаниеПрограммы);
//	
//	ЭлектроннаяПодпись.ЗаполнитьСписокПрограмм(ОписаниеПрограмм);
//
Процедура ЗаполнитьСписокПрограмм(ОписаниеПрограмм) Экспорт
	
	ПоставляемыеНастройки = Справочники.ПрограммыЭлектроннойПодписиИШифрования.ПоставляемыеНастройкиПрограмм();
	
	ВыполняетсяОбновлениеИнформационнойБазы =
		    ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы()
		Или ОбновлениеИнформационнойБазы.ЭтоВызовИзОбработчикаОбновления();
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Программы.Ссылка КАК Ссылка,
	|	Программы.ИмяПрограммы,
	|	Программы.ТипПрограммы
	|ИЗ
	|	Справочник.ПрограммыЭлектроннойПодписиИШифрования КАК Программы";
	
	Блокировка = Новый БлокировкаДанных;
	Блокировка.Добавить("Справочник.ПрограммыЭлектроннойПодписиИШифрования");
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		Выгрузка = Запрос.Выполнить().Выгрузить();
		
		Для Каждого ОписаниеПрограммы Из ОписаниеПрограмм Цикл
			Отбор = Новый Структура;
			Отбор.Вставить("ИмяПрограммы", ОписаниеПрограммы.ИмяПрограммы);
			Отбор.Вставить("ТипПрограммы", ОписаниеПрограммы.ТипПрограммы);
			
			Строки = Выгрузка.НайтиСтроки(Отбор);
			Если Строки.Количество() > 0 Тогда
				ПрограммаОбъект = Строки[0].Ссылка.ПолучитьОбъект();
			Иначе
				ПрограммаОбъект = Справочники.ПрограммыЭлектроннойПодписиИШифрования.СоздатьЭлемент();
			КонецЕсли;
			
			ОбновитьЗначение(ПрограммаОбъект.ПометкаУдаления, Ложь);
			Если Не ЗначениеЗаполнено(ПрограммаОбъект.РежимИспользования) Тогда
				ОбновитьЗначение(ПрограммаОбъект.РежимИспользования, Перечисления.РежимыИспользованияПрограммыЭлектроннойПодписи.Настроена);
			КонецЕсли;
			
			Строки = ПоставляемыеНастройки.НайтиСтроки(Отбор);
			Для Каждого КлючИЗначение Из ОписаниеПрограммы Цикл
				ИмяПоля = ?(КлючИЗначение.Ключ = "Представление", "Наименование", КлючИЗначение.Ключ);
				Если КлючИЗначение.Значение <> Неопределено Тогда
					ОбновитьЗначение(ПрограммаОбъект[ИмяПоля], КлючИЗначение.Значение, Истина);
				ИначеЕсли Строки.Количество() > 0 Тогда
					ОбновитьЗначение(ПрограммаОбъект[ИмяПоля], Строки[0][КлючИЗначение.Ключ], Истина);
				КонецЕсли;
			КонецЦикла;
			
			Если Не ПрограммаОбъект.Модифицированность() Тогда
				Продолжить;
			КонецЕсли;
			
			Если ВыполняетсяОбновлениеИнформационнойБазы Тогда
				ОбновлениеИнформационнойБазы.ЗаписатьДанные(ПрограммаОбъект);
			Иначе
				ПрограммаОбъект.Записать();
			КонецЕсли;
		КонецЦикла;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для использования в процедуре ЭлектроннаяПодпись.ЗаполнитьСписокПрограмм.
// Только для работы через средства платформы (МенеджерКриптографии).
//
// Параметры:
//  ИмяПрограммы - Строка - имя программы электронной подписи и шифрования.
//  ТипПрограммы - Строка - тип программы.
//
// Возвращаемое значение:
//  Структура - для передачи в процедуру ЭлектроннаяПодпись.ЗаполнитьСписокПрограмм 
//              (см. в ней описание свойств).
//
Функция НовоеОписаниеПрограммы(ИмяПрограммы = Неопределено, ТипПрограммы = Неопределено) Экспорт
	
	ОписаниеПрограммы = Новый Структура;
	ОписаниеПрограммы.Вставить("ИмяПрограммы", ИмяПрограммы);
	ОписаниеПрограммы.Вставить("ТипПрограммы", ТипПрограммы);
	ОписаниеПрограммы.Вставить("Представление");
	ОписаниеПрограммы.Вставить("АлгоритмПодписи");
	ОписаниеПрограммы.Вставить("АлгоритмХеширования");
	ОписаниеПрограммы.Вставить("АлгоритмШифрования");
	
	Возврат ОписаниеПрограммы;
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.КонвертXML.
Функция КонвертXML(Параметры) Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебныйКлиентСервер.КонвертXML(Параметры);
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.ПараметрыКонвертаXML.
Функция ПараметрыКонвертаXML() Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебныйКлиентСервер.ПараметрыКонвертаXML();
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.ПараметрыXMLDSig.
Функция ПараметрыXMLDSig() Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебныйКлиентСервер.ПараметрыXMLDSig();
	
КонецФункции

// См. ЭлектроннаяПодписьКлиент.ПараметрыCMS.
Функция ПараметрыCMS() Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебныйКлиентСервер.ПараметрыCMS();
	
КонецФункции

// Возвращает результат проверки удостоверяющего центра.
// 
// Параметры:
//  Сертификат - СертификатКриптографии
//  НаДату - Неопределено, Дата - если не указана, то проверка выполняется на дату сеанса.
//  ЭтоПроверкаПодписи - Булево - это проверка подписи, предупреждение (не ошибка) не будет заполнено.
// 
// Возвращаемое значение:
//  Структура - результат проверки удостоверяющего центра по умолчанию:
//   * Действует - Булево - аккредитованный УЦ действует на дату проверки или проверка не выполнялась (сертификат 
//                 неквалифицированный или УЦ не найден в списке аккредитованных)
//   * НайденВСпискеУдостоверяющихЦентров - Булево - сертификат, выданный УЦ является квалифицированным
//   * Государственный - Булево - относится к определенному списку УЦ, выдающих сертификаты, по которым не нужно
//                                выполнять некоторые проверки.
//   например, в России: УЦ ФНС России, Казначейства России, и Банка России.
//   * ЭтоКвалифицированныйСертификат - Булево - сертификат выдан в период аккредитации УЦ.
//   * Предупреждение - Структура - сообщение об ошибке/предупреждение для проверяемого сертификата:
//                       ** ТекстОшибки - Строка
//                       ** ВозможенПеревыпуск - Булево - можно подать заявление на новый сертификат из программы.
//                       ** Причина - Строка - причина ошибки для отображения в расширенной форме ошибки.
//                       ** Решение - Строка - рекомендация для отображения в расширенной форме ошибки.
//
Функция РезультатПроверкиУдостоверяющегоЦентраСертификата(Сертификат, НаДату = Неопределено, ЭтоПроверкаПодписи = Ложь) Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебный.РезультатПроверкиУдостоверяющегоЦентраСертификата(Сертификат, НаДату, ЭтоПроверкаПодписи);
	
КонецФункции

#КонецОбласти

// Возвращает доступность создания заявления на выпуск
// квалифицированных сертификатов для организаций и физических лиц.
// Требуется для скрытия команд, использующих процедуру
// ДобавитьСертификат общего модуля ЭлектроннаяПодписьКлиент
// в режиме создания заявления.
//
// Возвращаемое значение:
//  Структура:
//   * ДляФизическихЛиц - Булево
//   * ДляРуководителейЮридическихЛиц - Булево
//   * ДляСотрудниковЮридическихЛиц - Булево
//   * ДляИндивидуальныхПредпринимателей - Булево
//
Функция ДоступностьСозданияЗаявления() Экспорт
	
	Если Метаданные.ОбщиеМодули.Найти("ЭлектроннаяПодписьСлужебныйЛокализация") = Неопределено Тогда
		ЗаявлениеДоступно = ОбщиеНастройки().ЗаявлениеНаВыпускСертификатаДоступно;
		ДоступностьСозданияЗаявления = Новый Структура;
		ДоступностьСозданияЗаявления.Вставить("ДляФизическихЛиц", ЗаявлениеДоступно);
		ДоступностьСозданияЗаявления.Вставить("ДляРуководителейЮридическихЛиц", ЗаявлениеДоступно);
		ДоступностьСозданияЗаявления.Вставить("ДляСотрудниковЮридическихЛиц", ЗаявлениеДоступно);
		ДоступностьСозданияЗаявления.Вставить("ДляИндивидуальныхПредпринимателей", ЗаявлениеДоступно);
		Возврат ДоступностьСозданияЗаявления;
	КонецЕсли;
	
	МодульЭлектроннаяПодписьСлужебныйЛокализация = ОбщегоНазначения.ОбщийМодуль("ЭлектроннаяПодписьСлужебныйЛокализация");
	Возврат МодульЭлектроннаяПодписьСлужебныйЛокализация.ДоступностьСозданияЗаявления();
	
КонецФункции

#КонецОбласти

#Область СлужебныйПрограммныйИнтерфейс

// Подпись из двоичных данных.
// 
// Параметры:
//  ДанныеПодписи - ДвоичныеДанные
//                - Строка - адрес во временном хранилище
// 
// Возвращаемое значение:
//  ДвоичныеДанные - подпись из двоичных данных в кодировке DER
//
Функция ПодписьВКодировкеDER(ДанныеПодписи) Экспорт
	
	ДвоичныеДанные = ЭлектроннаяПодписьСлужебныйКлиентСервер.ДвоичныеДанныеИзДанных(ДанныеПодписи,
		"ЭлектроннаяПодпись.ПодписьВКодировкеDER");
	
	ПолноеИмяВременногоФайла = ПолучитьИмяВременногоФайла();
	ДвоичныеДанные.Записать(ПолноеИмяВременногоФайла);
	Текст = Новый ТекстовыйДокумент;
	Текст.Прочитать(ПолноеИмяВременногоФайла);
	
	Попытка
		УдалитьФайлы(ПолноеИмяВременногоФайла);
	Исключение
		ЗаписьЖурналаРегистрации(
			НСтр("ru = 'Электронная подпись.Удаление временного файла'",
				ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Ошибка, , ,
			ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
	КонецПопытки;
	
	СтрокаBase64 = Неопределено;
	Если Текст.КоличествоСтрок() > 3 И СтрНачинаетсяС(Текст.ПолучитьСтроку(1), "-----BEGIN")
		И СтрНачинаетсяС(Текст.ПолучитьСтроку(Текст.КоличествоСтрок()), "-----END") Тогда
		Текст.УдалитьСтроку(1);
		Текст.УдалитьСтроку(Текст.КоличествоСтрок());
		СтрокаBase64 = Текст.ПолучитьТекст();
	ИначеЕсли СтрНачинаетсяС(Текст.ПолучитьСтроку(1), "MI") Тогда
		СтрокаBase64 = Текст.ПолучитьТекст();
	КонецЕсли;
	
	Если СтрокаBase64 <> Неопределено Тогда
		Попытка
			ДвоичныеДанные = Base64Значение(СтрокаBase64);
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось получить данные из файла подписи по причине:
						|%1'"), ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
		КонецПопытки;
	КонецЕсли;
	
	Возврат ДвоичныеДанные;
	
КонецФункции

// Возвращает признак доступности просмотра зашифрованных данных.
//
// Возвращаемое значение:
//  Булево - если Истина, просмотр зашифрованных данных доступен.
//
Функция РасшифровкаДанных() Экспорт
	
	Возврат ШифрованиеИРасшифровкаДанных()
		Или ИспользоватьШифрование() И Пользователи.РолиДоступны("РасшифровкаДанных");
		
КонецФункции

// Возвращает признак доступности шифрования данных.
//
// Возвращаемое значение:
//  Булево - если Истина, шифрование данных доступно.
//
Функция ШифрованиеИРасшифровкаДанных() Экспорт
	
	Возврат ИспользоватьШифрование() И Пользователи.РолиДоступны("ШифрованиеИРасшифровкаДанных");
	
КонецФункции

// Возвращает признак доступности добавления электронных подписей.
//
// Возвращаемое значение:
//  Булево - если Истина, добавление электронных подписей доступно.
//
Функция ДобавлениеИзменениеЭлектронныхПодписей() Экспорт
	
	// АПК:515-выкл
	// Роль не содержит прав на объекты метаданных, кроме общей формы ДобавлениеЭлектроннойПодписиИзФайла.
	Возврат ИспользоватьЭлектронныеПодписи() И Пользователи.РолиДоступны("ДобавлениеИзменениеЭлектронныхПодписей");
	// АПК:515-вкл
	
КонецФункции

// Возвращает признак доступности управления оповещениями о сроке действия сертификатов
// и напоминаниями о получении сертификата в заявлении.
//
// Возвращаемое значение:
//  Булево - если Истина, управление оповещениями доступно.
//
Функция УправлениеОповещениямиОСертификатах() Экспорт
	
	Возврат ПравоДоступа("Изменение", Метаданные.Справочники.СертификатыКлючейЭлектроннойПодписиИШифрования)
		Или ДобавлениеИзменениеЭлектронныхПодписей() Или РасшифровкаДанных();
	
КонецФункции


// Возвращает настройки текущего пользователя для работы с электронной подписью.
//
// Возвращаемое значение:
//   Структура - персональные настройки для работы с электронной подписью:
//       * ДействияПриСохраненииСЭП - Строка - что делать при сохранении файлов с электронной подписью:
//           "Спрашивать" - показывать диалог выбора подписей для сохранения;
//           "СохранятьВсеПодписи" - всегда все подписи.
//       * ПутиКПрограммамЭлектроннойПодписиИШифрования - Соответствие из КлючИЗначение:
//           ** Ключ     - СправочникСсылка.ПрограммыЭлектроннойПодписиИШифрования - программа.
//           ** Значение - Строка - путь к программе на компьютере пользователя.
//       * РасширениеДляФайловПодписи - Строка - расширение для файлов ЭП.
//       * РасширениеДляЗашифрованныхФайлов - Строка - расширение для зашифрованных файлов.
//
// См. также:
//   ОбщаяФорма.НастройкиЭлектроннойПодписиИШифрования - место определения данных параметров и
//   их текстовые описания.
//
Функция ПерсональныеНастройки() Экспорт
	
	ПерсональныеНастройки = Новый Структура;
	// Начальные значения.
	ПерсональныеНастройки.Вставить("ДействияПриСохраненииСЭП", "Спрашивать");
	ПерсональныеНастройки.Вставить("ПутиКПрограммамЭлектроннойПодписиИШифрования", Новый Соответствие);
	ПерсональныеНастройки.Вставить("РасширениеДляФайловПодписи", "p7s");
	ПерсональныеНастройки.Вставить("РасширениеДляЗашифрованныхФайлов", "p7m");
	ПерсональныеНастройки.Вставить("СохранятьСертификатВместеСПодписью", Ложь);
	
	КлючПодсистемы = ЭлектроннаяПодписьСлужебный.КлючХраненияНастроек();
	
	Для Каждого КлючИЗначение Из ПерсональныеНастройки Цикл
		СохраненноеЗначение = ОбщегоНазначения.ХранилищеОбщихНастроекЗагрузить(КлючПодсистемы,
			КлючИЗначение.Ключ);
		
		Если ЗначениеЗаполнено(СохраненноеЗначение)
		   И ТипЗнч(КлючИЗначение.Значение) = ТипЗнч(СохраненноеЗначение) Тогда
			
			ПерсональныеНастройки.Вставить(КлючИЗначение.Ключ, СохраненноеЗначение);
		КонецЕсли;
	КонецЦикла;
	
	Возврат ПерсональныеНастройки;
	
КонецФункции

// Возвращает зашифрованные данные.
// 
// Параметры:
//  Данные - ДвоичныеДанные
//  Сертификат - СправочникСсылка.СертификатыКлючейЭлектроннойПодписиИШифрования
// 
// Возвращаемое значение:
//  ДвоичныеДанные
//
Функция Зашифровать(Данные, Сертификат) Экспорт
	
	Если Не ОбщиеНастройки().ИспользоватьШифрование Тогда
		ВызватьИсключение НСтр("ru = 'В программе не включено использование шифрования.'");
	КонецЕсли;
	
	СписокОшибок = Новый Массив;

	ЗначенияРеквизитов = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
		Сертификат, "Программа, ДанныеСертификата");
	
	Программа = ЗначенияРеквизитов.Программа;
	
	Попытка
		ДвоичныеДанныеСертификата = ЗначенияРеквизитов.ДанныеСертификата.Получить();
		СертификатКриптографии = Новый СертификатКриптографии(ДвоичныеДанныеСертификата);
	Исключение
		ОписаниеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось получить данные сертификата ""%1""
			           |из информационной базы по причине:
			           |%2'"),
			Сертификат,
			ОписаниеОшибки);
	КонецПопытки;
	
	МенеджерКриптографии = Неопределено;
	
	Если ЗначениеЗаполнено(ЗначенияРеквизитов.Программа) Тогда
		
		Если ТипЗнч(ЗначенияРеквизитов.Программа) = ЭлектроннаяПодписьСлужебный.ТипПрограммыСервисаПодписи() Тогда
			
			Возврат ЭлектроннаяПодписьСлужебный.ЗашифроватьВСервисеОблачнойПодписи(
				Данные, ДвоичныеДанныеСертификата, ЗначенияРеквизитов.Программа);
		
		ИначеЕсли Программа = ЭлектроннаяПодписьСлужебный.ВстроенныйКриптопровайдер() Тогда
			
			Возврат ЭлектроннаяПодписьСлужебный.ЗашифроватьВстроеннымКриптопровайдером(
				Данные, ДвоичныеДанныеСертификата);
			
		ИначеЕсли Не ОбщегоНазначения.РазделениеВключено()
			И (ОбщиеНастройки().СоздаватьЭлектронныеПодписиНаСервере Или ОбщегоНазначения.ИнформационнаяБазаФайловая())
			Тогда

			ПараметрыСоздания = ЭлектроннаяПодписьСлужебный.ПараметрыСозданияМенеджераКриптографии();
			ПараметрыСоздания.Программа = ЗначенияРеквизитов.Программа;
			ПараметрыСоздания.ОписаниеОшибки = "";
			МенеджерКриптографии = ЭлектроннаяПодписьСлужебный.МенеджерКриптографии("Шифрование",
				ПараметрыСоздания);

			ОшибкаНаСервере = ПараметрыСоздания.ОписаниеОшибки;
			Если ЗначениеЗаполнено(ОшибкаНаСервере) Тогда
				ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось выполнить шифрование сертификатом ""%1"":
						 |%2'"), Сертификат, ОшибкаНаСервере);
			КонецЕсли;

			Если МенеджерКриптографии = Неопределено Тогда
				ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось выполнить шифрование сертификатом ""%1"":
						|Не удалось создать менеджер криптографии'"), Сертификат);
			КонецЕсли;

			Попытка
				ДвоичныеДанныеРезультата = ЭлектроннаяПодписьСлужебный.Зашифровать(Данные, СертификатКриптографии,
					МенеджерКриптографии);
				Возврат ДвоичныеДанныеРезультата;
			Исключение
				ОписаниеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
				ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось выполнить шифрование сертификатом ""%1"":
						 |%2'"), Сертификат, ОписаниеОшибки);
			КонецПопытки;
			
		КонецЕсли;
		
		СписокОшибок.Добавить(НСтр("ru = 'Недоступно шифрование на сервере.'"));

	КонецЕсли;
	
	АлгоритмПодписи = ЭлектроннаяПодписьСлужебныйКлиентСервер.АлгоритмПодписиСертификата(ДвоичныеДанныеСертификата);
	
	Если Не ОбщегоНазначения.РазделениеВключено() И (ОбщиеНастройки().СоздаватьЭлектронныеПодписиНаСервере
		Или ОбщегоНазначения.ИнформационнаяБазаФайловая()) Тогда
	
		ПараметрыСоздания = ЭлектроннаяПодписьСлужебный.ПараметрыСозданияМенеджераКриптографии();
		ПараметрыСоздания.ОписаниеОшибки = "";
		ПараметрыСоздания.АлгоритмПодписи = АлгоритмПодписи;
			
		МенеджерКриптографии = ЭлектроннаяПодписьСлужебный.МенеджерКриптографии("Шифрование", ПараметрыСоздания);
		
		ОписаниеОшибки = ПараметрыСоздания.ОписаниеОшибки;
		
		Если Не ЗначениеЗаполнено(ОписаниеОшибки) Тогда
			
			Если МенеджерКриптографии = Неопределено Тогда
				ОписаниеОшибки = НСтр("ru = 'Не удалось создать менеджер криптографии'");
			Иначе

				Попытка
					ДвоичныеДанныеРезультата = ЭлектроннаяПодписьСлужебный.Зашифровать(Данные, СертификатКриптографии,
						МенеджерКриптографии);
					Возврат ДвоичныеДанныеРезультата;
				Исключение
					ОписаниеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
				КонецПопытки;

			КонецЕсли;
			
		КонецЕсли;
		
		СписокОшибок.Добавить(ОписаниеОшибки);
		
	КонецЕсли;
	
	МожноШифроватьВОблаке = СтрНайти(АлгоритмПодписи, "GOST 34.10") <> 0;
	
	Если МожноШифроватьВОблаке Тогда
		
		Если ОбщиеНастройки().ЭтоМодельСервисаСДоступнымУсовершенствованием Тогда
			Попытка
				ДвоичныеДанныеРезультата = ЭлектроннаяПодписьСлужебный.ЗашифроватьВСервисеОблачнойПодписи(Данные,
				ДвоичныеДанныеСертификата);
				Возврат ДвоичныеДанныеРезультата;
			Исключение
				ОписаниеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
				СписокОшибок.Добавить(ОписаниеОшибки);
			КонецПопытки;
		КонецЕсли;
		
		Если ЭлектроннаяПодписьСлужебный.ИспользоватьЭлектроннуюПодписьВМоделиСервиса() Тогда
			Попытка
				ДвоичныеДанныеРезультата = ЭлектроннаяПодписьСлужебный.ЗашифроватьВстроеннымКриптопровайдером(Данные, ДвоичныеДанныеСертификата);
				Возврат ДвоичныеДанныеРезультата;
			Исключение
				ОписаниеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
				СписокОшибок.Добавить(ОписаниеОшибки);
			КонецПопытки;
		КонецЕсли;
		
	КонецЕсли;
	
	Если ЗначениеЗаполнено(СписокОшибок) Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось выполнить шифрование сертификатом ""%1"":
					 |%2'"), Сертификат, СтрСоединить(СписокОшибок, Символы.ПС));
	КонецЕсли;

	ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось выполнить шифрование сертификатом ""%1""'"), Сертификат);
КонецФункции

#Область ОбработчикиРегламентныхЗаданий

// Локализация

// Регламентное задание.
Процедура ОбновлениеСостоянияЗаявленияНаВыпускСертификата(Сертификат) Экспорт

	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(
			Метаданные.РегламентныеЗадания.ОбновлениеСостоянияЗаявленияНаВыпускСертификата);
	ОбработкаЗаявлениеНаВыпускНовогоКвалифицированногоСертификата = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(
				"Обработка.ЗаявлениеНаВыпускНовогоКвалифицированногоСертификата");
	ОбработкаЗаявлениеНаВыпускНовогоКвалифицированногоСертификата.ОбновлениеСостоянияЗаявленияРегламентноеЗадание(
		Сертификат);

КонецПроцедуры
	
// Конец Локализация

// Регламентное задание.
Процедура ПродлениеДостоверностиПодписей() Экспорт
	
	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(
			Метаданные.РегламентныеЗадания.ПродлениеДостоверностиПодписей);
	
	Если Не ИспользоватьЭлектронныеПодписи() Или Не ДоступнаУсовершенствованнаяПодпись() Тогда
		Возврат;
	КонецЕсли;
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Если Не ОбщиеНастройки().ЭтоМодельСервисаСДоступнымУсовершенствованием Тогда
			Возврат;
		Иначе
			НастройкиСлужебнойУчетнойЗаписи = ЭлектроннаяПодписьСлужебный.НастройкиСлужебнойУчетнойЗаписиДляУсовершенствованияПодписей();
			Если ЗначениеЗаполнено(НастройкиСлужебнойУчетнойЗаписи.Ошибка) Тогда
				ВызватьИсключение(НастройкиСлужебнойУчетнойЗаписи.Ошибка);
			КонецЕсли;
		КонецЕсли;
		ТипПодписиКриптографииПоУмолчанию = Константы.ТипПодписиКриптографииПоУмолчанию.Получить();
		Если ТипПодписиКриптографииПоУмолчанию <> Перечисления.ТипыПодписиКриптографии.СМеткойДоверенногоВремениCAdEST Тогда
			ЭлектроннаяПодписьСлужебный.ИзменитьРегламентноеЗаданиеПродлениеДостоверностиПодписей(,Ложь,ТипПодписиКриптографииПоУмолчанию);
			Возврат;
		КонецЕсли;
		ТребуетсяДобавитьАрхивныеМетки = Ложь;
	Иначе
		ТребуетсяДобавитьАрхивныеМетки = Константы.ДобавлятьМеткиВремениАвтоматически.Получить();
		ТипПодписиКриптографииПоУмолчанию = Константы.ТипПодписиКриптографииПоУмолчанию.Получить();
	КонецЕсли;
	
	ТребуетсяУсовершенствоватьПодписи = Константы.УсовершенствоватьПодписиАвтоматически.Получить() = 1;
	
	Если (Не ТребуетсяУсовершенствоватьПодписи
		Или (ТипПодписиКриптографииПоУмолчанию <> Перечисления.ТипыПодписиКриптографии.СМеткойДоверенногоВремениCAdEST
		И ТипПодписиКриптографииПоУмолчанию <> Перечисления.ТипыПодписиКриптографии.АрхивнаяCAdESAv3))
		И Не ТребуетсяДобавитьАрхивныеМетки Тогда
		Возврат;
	КонецЕсли;
	
	ПараметрыВыполнения = Новый Структура;
	ПараметрыВыполнения.Вставить("ТипПодписи");
	ПараметрыВыполнения.Вставить("ТребуетсяДобавитьАрхивныеМетки");
	ПараметрыВыполнения.Вставить("АдресаСерверовМетокВремени");
	ПараметрыВыполнения.Вставить("ПараметрыПодписиCAdEST");
	ПараметрыВыполнения.Вставить("СлужебнаяУчетнаяЗаписьDSS");
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		Если ТребуетсяУсовершенствоватьПодписи Или ТребуетсяДобавитьАрхивныеМетки Тогда
			АдресаСерверовМетокВремени = ОбщиеНастройки().АдресаСерверовМетокВремени;
			Если АдресаСерверовМетокВремени.Количество() = 0 Тогда
				ВызватьИсключение НСтр("ru = 'Не указаны адреса серверов меток времени.'");
			КонецЕсли;
		КонецЕсли;
		ПараметрыВыполнения.АдресаСерверовМетокВремени = АдресаСерверовМетокВремени;
	Иначе
		ПараметрыВыполнения.СлужебнаяУчетнаяЗаписьDSS = НастройкиСлужебнойУчетнойЗаписи.СлужебнаяУчетнаяЗаписьDSS;
		ПараметрыВыполнения.ПараметрыПодписиCAdEST = НастройкиСлужебнойУчетнойЗаписи.ПараметрыПодписиCAdEST;
	КонецЕсли;
	
	Если ТребуетсяУсовершенствоватьПодписи Тогда
		ПараметрыВыполнения.Вставить("ТипПодписи", ТипПодписиКриптографииПоУмолчанию);
	КонецЕсли;
	ПараметрыВыполнения.Вставить("ТребуетсяДобавитьАрхивныеМетки", ТребуетсяДобавитьАрхивныеМетки);

	// Пока нет возможности в DSS определить начальный тип подписи.
	Если ПараметрыВыполнения.СлужебнаяУчетнаяЗаписьDSS = Неопределено Тогда
		// Необработанные подписи обрабатываем в первую очередь.
		ПараметрыЗапроса = Новый Структура;
		ПараметрыЗапроса.Вставить("РегламентноеЗадание", Истина);
		ПараметрыЗапроса.Вставить("НеобработанныеПодписи", Истина);
		ЭлектроннаяПодписьСлужебный.ВыполнитьОбработкуДанныхРегламентнымЗаданием(ПараметрыЗапроса, ПараметрыВыполнения);
	КонецЕсли;
	
	Если ТребуетсяУсовершенствоватьПодписи
		И (ТипПодписиКриптографииПоУмолчанию = Перечисления.ТипыПодписиКриптографии.СМеткойДоверенногоВремениCAdEST
			Или ТипПодписиКриптографииПоУмолчанию = Перечисления.ТипыПодписиКриптографии.АрхивнаяCAdESAv3) Тогда
		
		ПараметрыЗапроса = Новый Структура;
		ПараметрыЗапроса.Вставить("РегламентноеЗадание", Истина);
		ПараметрыЗапроса.Вставить("ТребуетсяУсовершенствоватьПодписи", ТребуетсяУсовершенствоватьПодписи);
		ПараметрыЗапроса.Вставить("УсовершенствоватьДоТипа", ТипПодписиКриптографииПоУмолчанию);
		
		ЭлектроннаяПодписьСлужебный.ВыполнитьОбработкуДанныхРегламентнымЗаданием(ПараметрыЗапроса, ПараметрыВыполнения);
	КонецЕсли;
	
	Если ТребуетсяДобавитьАрхивныеМетки Тогда
		ПараметрыЗапроса = Новый Структура;
		ПараметрыЗапроса.Вставить("РегламентноеЗадание", Истина);
		ПараметрыЗапроса.Вставить("ТребуетсяДобавитьАрхивныеМетки", ТребуетсяДобавитьАрхивныеМетки);
		
		ЭлектроннаяПодписьСлужебный.ВыполнитьОбработкуДанныхРегламентнымЗаданием(ПараметрыЗапроса, ПараметрыВыполнения);
	КонецЕсли;

КонецПроцедуры

#КонецОбласти

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// Возвращает общие настройки всех пользователей для работы с электронной подписью.
//
// Возвращаемое значение: 
//   ФиксированнаяСтруктура - общие настройки подсистемы для работы с электронной подписью:
//     * ИспользоватьЭлектронныеПодписи       - Булево - если Истина, то электронный подписи используются.
//     * ИспользоватьШифрование               - Булево - если Истина, то шифрование используются.
//     * ПроверятьЭлектронныеПодписиНаСервере - Булево - если Истина, то электронные подписи и
//                                                       сертификаты проверяются на сервере.
//     * СоздаватьЭлектронныеПодписиНаСервере - Булево - если Истина, то электронные подписи создаются
//                                                       сначала на сервере, а в случае неудачи на клиенте.
//
//     * ОписанияПрограмм - ФиксированныйМассив из см. ЭлектроннаяПодписьСлужебныйПовтИсп.ОписаниеПрограммы -
//                          сведения о поддерживаемых программах криптографии.
//
//     * ОписанияПрограммПоСсылке - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - СправочникСсылка.ПрограммыЭлектроннойПодписиИШифрования
//         ** Значение - см. ЭлектроннаяПодписьСлужебныйПовтИсп.ОписаниеПрограммы
//
// См. также:
//   ОбщаяФорма.НастройкиЭлектроннойПодписиИШифрования - место определения данных параметров и
//   их текстовые описания.
//
Функция ОбщиеНастройки() Экспорт
	
	Возврат ЭлектроннаяПодписьСлужебныйПовтИсп.ОбщиеНастройки();
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Вспомогательные процедуры и функции.

// Для процедуры ДобавитьПодпись.
Процедура ДобавитьСтрокиПодписи(ОбъектДанных, СвойстваПодписей, СообщениеЖурналаРегистрации)
	
	УстановитьПривилегированныйРежим(Истина);

	ПорядковыйНомер = 1;

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	КОЛИЧЕСТВО(ЭлектронныеПодписи.ПодписанныйОбъект) КАК ПоследнийПорядковыйНомер
	|ИЗ
	|	РегистрСведений.ЭлектронныеПодписи КАК ЭлектронныеПодписи
	|ГДЕ
	|	ЭлектронныеПодписи.ПодписанныйОбъект = &ПодписанныйОбъект";

	Запрос.УстановитьПараметр("ПодписанныйОбъект", ОбъектДанных.Ссылка);

	РезультатЗапроса = Запрос.Выполнить();

	ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		ПорядковыйНомер = ВыборкаДетальныеЗаписи.ПоследнийПорядковыйНомер + 1;
	КонецЦикла;

	Если ТипЗнч(СвойстваПодписей) = Тип("Массив") Тогда
		МассивСвойствПодписи = СвойстваПодписей;
	Иначе
		МассивСвойствПодписи = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(СвойстваПодписей);
	КонецЕсли;

	Для Каждого СвойстваПодписи Из МассивСвойствПодписи Цикл

		НоваяЗапись = РегистрыСведений.ЭлектронныеПодписи.СоздатьМенеджерЗаписи();
		
		НовыеСвойстваПодписи = ЭлектроннаяПодписьКлиентСервер.НовыеСвойстваПодписи();
		ЗаполнитьЗначенияСвойств(НовыеСвойстваПодписи, СвойстваПодписи);
		ЗаполнитьЗначенияСвойств(НоваяЗапись, СвойстваПодписи, , "Подпись, Сертификат");

		НоваяЗапись.ПодписанныйОбъект = ОбъектДанных.Ссылка;
		НоваяЗапись.Подпись    = Новый ХранилищеЗначения(СвойстваПодписи.Подпись, Новый СжатиеДанных(9));
		Если ТипЗнч(СвойстваПодписи.Сертификат) = Тип("ХранилищеЗначения") Тогда
			НоваяЗапись.Сертификат = СвойстваПодписи.Сертификат;
		Иначе
			НоваяЗапись.Сертификат = Новый ХранилищеЗначения(СвойстваПодписи.Сертификат, Новый СжатиеДанных(9));
		КонецЕсли;

		НоваяЗапись.ПорядковыйНомер = ПорядковыйНомер;

		Если Не ЗначениеЗаполнено(НоваяЗапись.УстановившийПодпись) Тогда
			НоваяЗапись.УстановившийПодпись = Пользователи.АвторизованныйПользователь();
		КонецЕсли;

		ДатаПодписи = Неопределено;
		Если ЗначениеЗаполнено(ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(
				СвойстваПодписи, "НеподтвержденнаяДатаПодписи", Неопределено)) Тогда
			ДатаПодписи = СвойстваПодписи.НеподтвержденнаяДатаПодписи;
		ИначеЕсли ЗначениеЗаполнено(НоваяЗапись.ТипПодписи) Тогда
			ДатаПодписи = ДатаПодписания(СвойстваПодписи.Подпись);
		Иначе
			ПараметрыПодписи = ЭлектроннаяПодписьСлужебныйКлиентСервер.СвойстваПодписиИзДвоичныхДанных(
				СвойстваПодписи.Подпись, ЭлектроннаяПодписьСлужебный.ДобавкаВремени());
			Если ЗначениеЗаполнено(ПараметрыПодписи.ДатаПодписания) Тогда
				ДатаПодписи = ПараметрыПодписи.ДатаПодписания;
			КонецЕсли;
			НоваяЗапись.ТипПодписи = ПараметрыПодписи.ТипПодписи;
		КонецЕсли;

		Если ДатаПодписи <> Неопределено Тогда
			НоваяЗапись.ДатаПодписи = ДатаПодписи;

		ИначеЕсли Не ЗначениеЗаполнено(НоваяЗапись.ДатаПодписи) Тогда
			НоваяЗапись.ДатаПодписи = ТекущаяДатаСеанса();
		КонецЕсли;
		
		Если Не ЗначениеЗаполнено(НоваяЗапись.ИдентификаторПодписи) Тогда
			НоваяЗапись.ИдентификаторПодписи = Новый УникальныйИдентификатор;
		КонецЕсли;

		СообщениеЖурналаРегистрации = ЭлектроннаяПодписьСлужебный.СведенияОПодписиДляЖурналаРегистрации(
				НоваяЗапись.ДатаПодписи, СвойстваПодписи);

		НоваяЗапись.Записать();
		
		// Локализация
		Если ЗначениеЗаполнено(НовыеСвойстваПодписи.РезультатПроверкиПодписиПоМЧД)
			И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.МашиночитаемыеДоверенности") Тогда
			
			МодульМашиночитаемыеДоверенностиФНССлужебный = ОбщегоНазначения.ОбщийМодуль("МашиночитаемыеДоверенностиФНССлужебный");
			МодульМашиночитаемыеДоверенностиФНССлужебный.ДобавитьМашиночитаемуюДоверенностьПодписи(
				ОбъектДанных.Ссылка, НоваяЗапись.ИдентификаторПодписи, НовыеСвойстваПодписи.РезультатПроверкиПодписиПоМЧД);
			
		КонецЕсли;
		// Конец Локализация

		ЗаписьЖурналаРегистрации(
				НСтр("ru = 'Электронная подпись.Добавление подписи'", ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Информация, ОбъектДанных.Метаданные(), ОбъектДанных.Ссылка,
			СообщениеЖурналаРегистрации, РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);

		ПорядковыйНомер = ПорядковыйНомер + 1;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры УдалитьПодпись.
Процедура УдалитьСтрокиПодписи(ПодписанныйОбъект, ПорядковыеНомера, СообщениеЖурналаРегистрации)
	
	ЕстьПраваНаУдалениеЧужихПодписей = Пользователи.ЭтоПолноправныйПользователь() 
		Или Пользователи.РолиДоступны("УдалениеЭлектронныхПодписей");

	УстановитьПривилегированныйРежим(Истина);

	Если ТипЗнч(ПорядковыеНомера) = Тип("Массив") Тогда
		МассивПорядковыхНомеров = ПорядковыеНомера;
	Иначе
		МассивПорядковыхНомеров = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ПорядковыеНомера);
	КонецЕсли;

	Запрос = Новый Запрос;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.МашиночитаемыеДоверенности") Тогда
		МодульМашиночитаемыеДоверенностиФНССлужебный = ОбщегоНазначения.ОбщийМодуль("МашиночитаемыеДоверенностиФНССлужебный");
		Запрос.Текст = МодульМашиночитаемыеДоверенностиФНССлужебный.ТекстЗапросаДляУдаленияЭлектронныхПодписей();
	Иначе
		Запрос.Текст =
			"ВЫБРАТЬ
			|	ЭлектронныеПодписи.ПорядковыйНомер КАК ПорядковыйНомер,
			|	ЭлектронныеПодписи.ПодписанныйОбъект КАК ПодписанныйОбъект,
			|	0 КАК ЕстьПодписиПоМЧД
			|ИЗ
			|	РегистрСведений.ЭлектронныеПодписи КАК ЭлектронныеПодписи
			|ГДЕ
			|	ЭлектронныеПодписи.ПорядковыйНомер В(&МассивПорядковыхНомеров)
			|	И ЭлектронныеПодписи.ПодписанныйОбъект = &ПодписанныйОбъект
			|
			|УПОРЯДОЧИТЬ ПО
			|	ПорядковыйНомер УБЫВ";
	КонецЕсли;

	Запрос.УстановитьПараметр("МассивПорядковыхНомеров", МассивПорядковыхНомеров);
	Запрос.УстановитьПараметр("ПодписанныйОбъект", ПодписанныйОбъект.Ссылка);

	РезультатЗапроса = Запрос.Выполнить();

	ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

	Если ВыборкаДетальныеЗаписи.Количество() <> МассивПорядковыхНомеров.Количество() Тогда
		ВызватьИсключение НСтр("ru = 'Строка с подписью не существует.'");
	КонецЕсли;

	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		МенеджерЗаписи = РегистрыСведений.ЭлектронныеПодписи.СоздатьМенеджерЗаписи();
		ЗаполнитьЗначенияСвойств(МенеджерЗаписи, ВыборкаДетальныеЗаписи);

		МенеджерЗаписи.Прочитать();

		ЕстьПрава = ЕстьПраваНаУдалениеЧужихПодписей Или МенеджерЗаписи.УстановившийПодпись
			= Пользователи.АвторизованныйПользователь();

		СвойстваПодписи = Новый Структура;
		СвойстваПодписи.Вставить("Сертификат", МенеджерЗаписи.Сертификат.Получить());
		СвойстваПодписи.Вставить("КомуВыданСертификат", МенеджерЗаписи.КомуВыданСертификат);

		СообщениеЖурналаРегистрации = ЭлектроннаяПодписьСлужебный.СведенияОПодписиДляЖурналаРегистрации(
			МенеджерЗаписи.ДатаПодписи, СвойстваПодписи);

		Если ЕстьПрава Тогда
			МенеджерЗаписи.Удалить();
		Иначе
			ВызватьИсключение НСтр("ru = 'Недостаточно прав на удаление подписи.'");
		КонецЕсли;
		
		Если ВыборкаДетальныеЗаписи.ЕстьПодписиПоМЧД > 0 Тогда
			МодульМашиночитаемыеДоверенностиФНССлужебный.УдалитьМашиночитаемуюДоверенностьПодписи(
				ПодписанныйОбъект.Ссылка, ВыборкаДетальныеЗаписи.ИдентификаторПодписи);
		КонецЕсли;

		ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Электронная подпись.Удаление подписи'", ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Информация, ПодписанныйОбъект.Метаданные(), ПодписанныйОбъект.Ссылка,
			СообщениеЖурналаРегистрации, РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);

	КонецЦикла;
	
КонецПроцедуры

// Для процедуры УдалитьПодпись.
Процедура ОбновитьНумерациюПодписей(ПодписанныйОбъект)
	
	УстановитьПривилегированныйРежим(Истина);
	
	ПодписанЭП = Ложь;
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЭлектронныеПодписи");
	ЭлементБлокировки.УстановитьЗначение("ПодписанныйОбъект", ПодписанныйОбъект.Ссылка);
	
	НачатьТранзакцию();
	Попытка
		
		Блокировка.Заблокировать();
		
		НаборЗаписей = РегистрыСведений.ЭлектронныеПодписи.СоздатьНаборЗаписей();
		НаборЗаписей.Отбор.ПодписанныйОбъект.Установить(ПодписанныйОбъект.Ссылка);
		НаборЗаписей.Прочитать();
		
		ПорядковыйНомер = 1;
		Для Каждого ЭлектроннаяПодписьОбъекта Из НаборЗаписей Цикл
			ЭлектроннаяПодписьОбъекта.ПорядковыйНомер = ПорядковыйНомер;
			ПорядковыйНомер = ПорядковыйНомер + 1;
			ПодписанЭП = Истина;
		КонецЦикла;
		
		Если ПодписанныйОбъект.ПодписанЭП <> ПодписанЭП Тогда
			ПодписанныйОбъект.ПодписанЭП = ПодписанЭП;
		КонецЕсли;
		
		НаборЗаписей.Записать(Истина);
		ЗафиксироватьТранзакцию();
		
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	УстановитьПривилегированныйРежим(Ложь);
	
КонецПроцедуры

// Для процедуры ЗаписатьСертификатВСправочник.
Процедура ОбновитьЗначение(СтароеЗначение, НовоеЗначение, ПропускатьНеопределенныеЗначения = Ложь)
	
	Если НовоеЗначение = Неопределено И ПропускатьНеопределенныеЗначения Тогда
		Возврат;
	КонецЕсли;
	
	Если СтароеЗначение <> НовоеЗначение Тогда
		СтароеЗначение = НовоеЗначение;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаписатьСертификатВСправочник.
// 
// Параметры:
//  Пользователь - СправочникСсылка.Пользователи - пользователь
//  Пользователи - СправочникТабличнаяЧасть.СертификатыКлючейЭлектроннойПодписиИШифрования.Пользователи - пользователи
//
Процедура ДобавитьПользователяВСертификат(Пользователь, Пользователи)
	
	Если Пользователи.Найти(Пользователь, "Пользователь") = Неопределено Тогда
		Пользователи.Добавить().Пользователь = Пользователь;
	КонецЕсли;
	
КонецПроцедуры

Процедура ПроверитьПараметрОбъект(Объект, ИмяПроцедуры, ТолькоСсылки = Ложь)
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "Объект", Объект,
		ЭлектроннаяПодписьСлужебныйПовтИсп.ТипыВладельцев(ТолькоСсылки));
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаСФайлами") Тогда
		МодульРаботаСФайламиСлужебный = ОбщегоНазначения.ОбщийМодуль("РаботаСФайламиСлужебный");
		МодульРаботаСФайламиСлужебный.ПроверитьФайлОбработан(Объект, ИмяПроцедуры);
	КонецЕсли;
	
КонецПроцедуры

Процедура ЗаполнитьРезультатПроверкиПодписи(
	Результат, РезультатСтруктура = Неопределено, ТребуетсяПроверка = Неопределено, СертификатОтозван = Ложь)
	
	Если РезультатСтруктура <> Неопределено Тогда
				
		РезультатСтруктура.Результат = Результат;
		
		Если Результат = Истина Тогда
			РезультатСтруктура.ПодписьВерна = Истина;
			РезультатСтруктура.ТребуетсяПроверка = Ложь;
			Возврат;
		КонецЕсли;
		
		Если ТребуетсяПроверка <> Неопределено Тогда
			РезультатСтруктура.ТребуетсяПроверка = ТребуетсяПроверка;
		КонецЕсли;
		
		Если РезультатСтруктура.ТребуетсяПроверка = Ложь Тогда
			
			Если СертификатОтозван Тогда
				РезультатСтруктура.Результат = ЭлектроннаяПодписьСлужебныйКлиентСервер.ТекстОшибкиДляОтозванногоСертификатаПодписи(
					РезультатСтруктура);
			КонецЕсли;
			РезультатСтруктура.СертификатОтозван = СертификатОтозван;
			РезультатСтруктура.ТребуетсяПроверка = ТребуетсяПроверка;
			
			РезультатСтруктура.ПодписьВерна = Ложь;
		КонецЕсли;
		
	КонецЕсли;
	
КонецПроцедуры

Функция ТребуетсяПроверкаПодписи(ОписаниеОшибки, РезультатСтруктура)
	
	ТребуетсяПроверка = Неопределено;
	Если ЗначениеЗаполнено(ОписаниеОшибки) И РезультатСтруктура <> Неопределено Тогда
		ОшибкаПоКлассификатору = ЭлектроннаяПодписьСлужебный.ОшибкаПоКлассификатору(ОписаниеОшибки, Истина);
		Если ОшибкаПоКлассификатору <> Неопределено Тогда
			ТребуетсяПроверка = ОшибкаПоКлассификатору.ТребуетПроверки;
		КонецЕсли;
	КонецЕсли;
	
	Возврат ТребуетсяПроверка;
	
КонецФункции

Функция СертификатПодписиОтозван(ОписаниеОшибки, РезультатСтруктура)
	
	СертификатОтозван = Ложь;
	Если ЗначениеЗаполнено(ОписаниеОшибки) И РезультатСтруктура <> Неопределено Тогда
		ОшибкаПоКлассификатору = ЭлектроннаяПодписьСлужебный.ОшибкаПоКлассификатору(ОписаниеОшибки, Истина);
		Если ОшибкаПоКлассификатору <> Неопределено Тогда
			СертификатОтозван = ОшибкаПоКлассификатору.СертификатОтозван;
		КонецЕсли;
	КонецЕсли;
	
	Возврат СертификатОтозван;
	
КонецФункции

#КонецОбласти